<!DOCTYPE html>
<html>

<head>
	<meta charset="utf-8">
	<meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
	<meta name="theme-color" content="#33474d">
	<title>失落的乐章</title>
	<link rel="stylesheet" href="/css/style.css" />
	
      <link rel="alternate" href="/atom.xml" title="失落的乐章" type="application/atom+xml">
    
</head>

<body>

	<header class="header">
		<nav class="header__nav">
			
				<a href="/archives" class="header__link">Archive</a>
			
				<a href="/tags" class="header__link">Tags</a>
			
				<a href="/atom.xml" class="header__link">RSS</a>
			
		</nav>
		<h1 class="header__title"><a href="/">失落的乐章</a></h1>
		<h2 class="header__subtitle">技术面前，永远都是学生。</h2>
	</header>

	<main>
		
	<span class="different-posts different-posts_earlier">📖 <a href="/page/37">earlier posts</a> 📖</span>




	<article>
	
		<h1><a href="/2017/10/12/Haproxy/4. haproxy 常用的8种算法/">haproxy 常用的8种算法</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/haproxy/">haproxy</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HAProxy的负载均衡算法现在也越来越多了，具体有如下8种：</p>
<ol>
<li>roundrobin，表示简单的轮询，这个不多说，这个是负载均衡基本都具备的；</li>
<li>static-rr，表示根据权重，建议关注；</li>
<li>leastconn，表示最少连接者先处理，建议关注；</li>
<li>source，表示根据请求源IP，这个跟Nginx的IP_hash机制类似，我们用其作为解决session问题的一种方法</li>
<li>ri，表示根据请求的URI；</li>
<li>rl_param，表示根据请求的URl参数’balance url_param’ requires an URL parameter name；</li>
<li>hdr(name)，表示根据HTTP请求头来锁定每一次HTTP请求；</li>
<li>rdp-cookie(name)，表示根据据cookie(name)来锁定并哈希每一次TCP请求。</li>
</ol>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Haproxy/3. hapronxy 的特性/">haproxy 的特性</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/haproxy/">haproxy</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;负载均衡软件中，硬件设备有：F5，Big-IP等</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;基于软件的：HAProxy, LVS, Nginx等，在软件发负载均衡中，又分为两种实现方式，分贝时基于操作系统的负载均衡如：lvs, 和基于第三方应用实现的软件负载均衡。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HAProxy 是一款提供高可用性、负载均衡以及基于TCP（第四层）和HTTP（第七层）应用的代理软件，支持虚拟主机，它是免费、快速并且可靠的一种解决方案。 HAProxy特别适用于那些负载特大的web站点，这些站点通常又需要会话保持或七层处理。HAProxy运行在时下的硬件上，完全可以支持数以万计的 并发连接。并且它的运行模式使得它可以很简单安全的整合进您当前的架构中， 同时可以保护你的web服务器不被暴露到网络上。它的优点如下：</p>
<ol>
<li>可靠性和稳定性非常好，可以与硬件F5负载均衡设备相媲美</li>
<li>最高可以同时维护40000-50000个并发连接，单位时间内处理的最大请求数为20000个，最大数据处理能力可达10Gbps。作为软件级别的负载均衡来说，HAProxy性能可以见一般</li>
<li>支持多于8种负载均衡算法，同时支持session保持</li>
<li>支持虚拟主机功能，这样实现web负载均衡更加灵活</li>
<li>从haproxy1.3版本后开始支持连接拒绝，全透明代理等功能，这些功能是其他负载均衡器锁不具备的。</li>
<li>haproxy拥有一个功能强大的服务器转台监控界面，通过此页面可以实时连接系统的运行状况，</li>
<li>haproxy拥有强大的acl支持，能给使用带来更大的方便。</li>
</ol>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Haproxy/1. HAProxy 配置文件结构/">haproxy 配置文件结构</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/haproxy/">haproxy</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HAProxy配置文件主要由5个部分组成，但是有些部分并不是必需的，可以根据需要选择相应的部分进行配置：</p>
<h2 id="1-global部分"><a href="#1-global部分" class="headerlink" title="1.global部分"></a>1.global部分</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;设定全局参数变量，属于进程级别的配置，通常和操作系统配置有关</p>
<h2 id="2-defaults部分"><a href="#2-defaults部分" class="headerlink" title="2.defaults部分"></a>2.defaults部分</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;模式参数的配置部分。在此部分设置的参数值，默认会自动引用到下面的frontend。backend和listen部分中，因此，如果某些参数属于公用的配置，只需要在defaults部分添加一次即可。如果在frontend、backend和listen部分中也配置了于defaults部分一样的参数，那么defaults部分参数对应的值自动覆盖</p>
<h2 id="3-frontend部分"><a href="#3-frontend部分" class="headerlink" title="3.frontend部分"></a>3.frontend部分</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;此部分用于设置接收用户请求的前段虚拟节点。frontend是在haproxy1.3版本以后才引入的一个组件，同事引入的还有backend组件。通过引入这些组件，在很大程度上简化了HAProxy配置文件的复杂性。frontend可以根据ACL规则直接指定要是用的后端backend。</p>
<h2 id="4-backend部分"><a href="#4-backend部分" class="headerlink" title="4.backend部分"></a>4.backend部分</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;此部分用于设置集群后端服务集群的配置，也就是用来添加一组真是服务器，以处理前端用户的请求。添加的真是服务器类似于LVS中的real server节点</p>
<h2 id="5-listen部分"><a href="#5-listen部分" class="headerlink" title="5. listen部分"></a>5. listen部分</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;此部分是frontend部分和backend部分的结合体。在HAProxy 1.3版本之前，HAProxy的所有配置选项都在这个部分上设置。为了保持兼容性，HAProxy新的版本仍然保留了listen组件的配置方式。目前在HAProxy中，两种配置方式任选其一即可。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Haproxy/2. LVS、Nginx、HaProxy 优缺点/">LVS、Nginx、HaProxy 优缺点</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/LVS/">LVS</a> <a class="article__tag-link" href="/tags/Nginx/">Nginx</a> <a class="article__tag-link" href="/tags/haproxy/">haproxy</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;搭建负载均衡高可用环境相对简单，主要是要理解其中原理。此文描述了三种负载均衡器的优缺点，以便在实际的生产应用中，按需求取舍。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;目前，在线上环境中应用较多的负载均衡器硬件有F5 BIG-IP,软件有LVS，Nginx及HAProxy,高可用软件有Heartbeat、Keepalived，成熟的架构有LVS+Keepalived、Nginx+Keepalived、HAProxy+keepalived及DRBD+Heartbeat.</p>
<h2 id="三种负载均衡器的优缺点说明如下："><a href="#三种负载均衡器的优缺点说明如下：" class="headerlink" title="三种负载均衡器的优缺点说明如下："></a>三种负载均衡器的优缺点说明如下：</h2><h3 id="LVS"><a href="#LVS" class="headerlink" title="LVS"></a>LVS</h3><h4 id="LVS的优点："><a href="#LVS的优点：" class="headerlink" title="LVS的优点："></a>LVS的优点：</h4><ol>
<li>抗负载能力强、工作在第4层仅作分发之用，没有流量的产生，这个特点也决定了它在负载均衡软件里的性能最强的；无流量，同时保证了均衡器IO的性能不会受到大流量的影响；</li>
<li>工作稳定，自身有完整的双机热备方案，如LVS+Keepalived和LVS+Heartbeat；</li>
<li>应用范围比较广，可以对所有应用做负载均衡；</li>
<li>配置性比较低，这是一个缺点也是一个优点，因为没有可太多配置的东西，所以并不需要太多接触，大大减少了人为出错的几率；</li>
</ol>
<h4 id="LVS的缺点："><a href="#LVS的缺点：" class="headerlink" title="LVS的缺点："></a>LVS的缺点：</h4><ol>
<li>软件本身不支持正则处理，不能做动静分离，这就凸显了Nginx/HAProxy+Keepalived的优势。</li>
<li>如果网站应用比较庞大，LVS/DR+Keepalived就比较复杂了，特别是后面有Windows Server应用的机器，实施及配置还有维护过程就比较麻烦，相对而言，Nginx/HAProxy+Keepalived就简单多了。</li>
</ol>
<hr>
<ul>
<li>LVS/DR如何处理请求报文的，会修改IP包内容吗？<ul>
<li>vs/dr本身不会关心IP层以上的信息，即使是端口号也是tcp/ip协议栈去判断是否正确，vs/dr本身主要做这么几个事：<ul>
<li>接收client的请求，根据你设定的负载均衡算法选取一台realserver的ip；</li>
<li>以选取的这个ip对应的mac地址作为目标mac，然后重新将IP包封装成帧转发给这台RS；</li>
<li>在hash table中记录连接信息。</li>
</ul>
</li>
</ul>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;vs/dr做的事情很少，也很简单，所以它的效率很高，不比硬件负载均衡设备差多少。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;数据包、数据帧的大致流向是这样的：client –&gt; VS –&gt; RS –&gt; client</p>
<pre><code>- 前面已作了回答，vs/dr不会修改IP包的内容.
</code></pre><ul>
<li>RealServer为什么要在lo接口上配置VIP？在出口网卡上配置VIP可以吗？<ul>
<li>既然要让RS能够处理目标地址为vip的IP包，首先必须要让RS能接收到这个包。</li>
</ul>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在lo上配置vip能够完成接收包并将结果返回client。</p>
<pre><code>- 答案是不可以将VIP设置在出口网卡上,否则会响应客户端的arp request,造成client/gateway arp table紊乱，以至于整个load balance都不能正常工作。
</code></pre><ul>
<li>RealServer为什么要抑制arp帧？<br>  &#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这个问题在上一问题中已经作了说明，这里结合实施命令进一步阐述。我们在具体实施部署的时候都会作如下调整： </li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">"1"</span> &gt;/proc/sys/net/ipv4/conf/lo/arp_ignore    </div><div class="line"><span class="built_in">echo</span> <span class="string">"2"</span> &gt;/proc/sys/net/ipv4/conf/lo/arp_announce    </div><div class="line"><span class="built_in">echo</span> <span class="string">"1"</span> &gt;/proc/sys/net/ipv4/conf/all/arp_ignore    </div><div class="line"><span class="built_in">echo</span> <span class="string">"2"</span> &gt;/proc/sys/net/ipv4/conf/all/arp_announce</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;相信很多人都不会弄懂它们的作用是什么，只知道一定得有。我这里也不打算拿出来详细讨论，只是作几点说明，就当是补充吧。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">"1"</span> &gt;/proc/sys/net/ipv4/conf/lo/arp_ignore    </div><div class="line"><span class="built_in">echo</span> <span class="string">"2"</span> &gt;/proc/sys/net/ipv4/conf/lo/arp_announce</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这两条是可以不用的，因为arp对逻辑接口没有意义。</p>
<ul>
<li>如果你的RS的外部网络接口是eth0，那么     </li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">"1"</span> &gt;/proc/sys/net/ipv4/conf/all/arp_ignore     </div><div class="line"><span class="built_in">echo</span> <span class="string">"2"</span> &gt;/proc/sys/net/ipv4/conf/all/arp_announce</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;其实真正要执行的是：      </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line"><span class="built_in">echo</span> <span class="string">"1"</span> &gt;/proc/sys/net/ipv4/conf/eth0/arp_ignore      </div><div class="line"><span class="built_in">echo</span> <span class="string">"2"</span> &gt;/proc/sys/net/ipv4/conf/eth0/arp_announce</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;所以我个人建议把上面两条也加到你的脚本里去，因为万一系统里上面两条默认的值不是0，那有可能是会出问题滴。</p>
<ul>
<li>LVS/DR load balancer（director）与RS为什么要在同一网段中？<br>  从第一个问题中大家应该明白vs/dr是如何将请求转发给RS的了吧？它是在数据链路层来实现的，所以director必须和RS在同一网段里面。</li>
<li>为什么director上lo接口除了VIP另外还要在eth0配一个ip（即DIP）？<ul>
<li>如果是用了keepalived等工具做HA或者Load Balance,则在健康检查时需要用到DIP。</li>
<li>没有健康检查机制的HA或者Load Balance则没有存在的实际意义。</li>
</ul>
</li>
<li>LVS/DR ip_forward需要开启吗？<br>  不需要。因为director跟realserver是同一个网段，无需开启转发。</li>
<li>director的vip的netmask一定要是255.255.255.255吗？<br>  lvs/dr里，director的vip的netmask 没必要设置为255.255.255.255，也不需要再去<br>  route add -host $VIP dev eth0:0<br>  director的vip本来就是要像正常的ip地址一样对外通告的,不要搞得这么特殊.          </li>
<li>LVS/DR如何进行tcp的三次握手？</li>
</ul>
<hr>
<h3 id="Nginx"><a href="#Nginx" class="headerlink" title="Nginx"></a>Nginx</h3><h4 id="Nginx的优点："><a href="#Nginx的优点：" class="headerlink" title="Nginx的优点："></a>Nginx的优点：</h4><ol>
<li>工作在OSI第7层，可以针对http应用做一些分流的策略。比如针对域名、目录结构。它的正则比HAProxy更为强大和灵活；</li>
<li>Nginx对网络的依赖非常小，理论上能ping通就就能进行负载功能，这个也是它的优势所在；</li>
<li>Nginx安装和配置比较简单，测试起来比较方便；</li>
<li>可以承担高的负载压力且稳定，一般能支撑超过几万次的并发量；</li>
<li>Nginx可以通过端口检测到服务器内部的故障，比如根据服务器处理网页返回的状态码、超时等等，并且会把返回错误的请求重新提交到另一个节点；</li>
<li>Nginx不仅仅是一款优秀的负载均衡器/反向代理软件，它同时也是功能强大的Web应用服务器。LNMP现在也是非常流行的web环境，大有和LAMP环境分庭抗礼之势，Nginx在处理静态页面、特别是抗高并发方面相对apache有优势；</li>
<li>Nginx现在作为Web反向加速缓存越来越成熟了，速度比传统的Squid服务器更快，有需求的朋友可以考虑用其作为反向代理加速器；</li>
</ol>
<h4 id="Nginx的缺点："><a href="#Nginx的缺点：" class="headerlink" title="Nginx的缺点："></a>Nginx的缺点：</h4><ol>
<li>Nginx不支持url来检测。</li>
<li>Nginx仅能支持http和Email，这个它的弱势。</li>
<li>Nginx的Session的保持，Cookie的引导能力相对欠缺。</li>
</ol>
<h3 id="HaProxy"><a href="#HaProxy" class="headerlink" title="HaProxy"></a>HaProxy</h3><h4 id="HAProxy的优点："><a href="#HAProxy的优点：" class="headerlink" title="HAProxy的优点："></a>HAProxy的优点：</h4><ol>
<li>HAProxy是支持虚拟主机的，可以工作在4、7层(支持多网段)；</li>
<li>能够补充Nginx的一些缺点比如Session的保持，Cookie的引导等工作；</li>
<li>支持url检测后端的服务器；</li>
<li>它跟LVS一样，本身仅仅就只是一款负载均衡软件；单纯从效率上来讲HAProxy更会比Nginx有更出色的负载均衡速度，在并发处理上也是优于Nginx的；</li>
<li>HAProxy可以对Mysql读进行负载均衡，对后端的MySQL节点进行检测和负载均衡，不过在后端的MySQL slaves数量超过10台时性能不如LVS；</li>
<li>HAProxy的算法较多，达到8种；</li>
</ol>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/7. 【Hadoop】HDFS的运行原理/">【Hadoop】HDFS的运行原理</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<h2 id="简介"><a href="#简介" class="headerlink" title="简介"></a>简介</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>HDFS</strong>（Hadoop Distributed File System ）Hadoop分布式文件系统。是根据google发表的论文翻版的。论文为GFS（Google File System）Google 文件系统（中文，英文）。</p>
<h3 id="HDFS有很多特点："><a href="#HDFS有很多特点：" class="headerlink" title="HDFS有很多特点："></a>HDFS有很多特点：</h3><ol>
<li>保存多个副本，且提供容错机制，副本丢失或宕机自动恢复。默认存3份。</li>
<li>运行在廉价的机器上。</li>
<li>适合大数据的处理。多大？多小？HDFS默认会将文件分割成block，64M为1个block。然后将block按键值对存储在HDFS上，并将键值对的映射存到内存中。如果小文件太多，那内存的负担会很重。</li>
</ol>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E7%9A%84%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86/01.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如上图所示，HDFS也是按照Master和Slave的结构。分NameNode、SecondaryNameNode、DataNode这几个角色。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>NameNode</strong>：是Master节点，是大领导。管理数据块映射；处理客户端的读写请求；配置副本策略；管理HDFS的名称空间；</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>SecondaryNameNode</strong>：是一个小弟，分担大哥namenode的工作量；是NameNode的冷备份；合并fsimage和fsedits然后再发给namenode。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>DataNode</strong>：Slave节点，奴隶，干活的。负责存储client发来的数据块block；执行数据块的读写操作。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>热备份</strong>：b是a的热备份，如果a坏掉。那么b马上运行代替a的工作。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>冷备份</strong>：b是a的冷备份，如果a坏掉。那么b不能马上代替a工作。但是b上存储a的一些信息，减少a坏掉之后的损失。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>fsimage</strong>:元数据镜像文件（文件系统的目录树。）</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>edits</strong>：元数据的操作日志（针对文件系统做的修改操作记录）</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>namenode内存中存储的是=fsimage+edits</strong></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;SecondaryNameNode负责定时默认1小时，从namenode上，获取fsimage和edits来进行合并，然后再发送给namenode。减少namenode的工作量。</p>
<h2 id="工作原理"><a href="#工作原理" class="headerlink" title="工作原理"></a>工作原理</h2><h3 id="写操作："><a href="#写操作：" class="headerlink" title="写操作："></a>写操作：</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E7%9A%84%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86/02.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有一个文件FileA，100M大小。Client将FileA写入到HDFS上。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS按默认配置。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS分布在三个机架上Rack1，Rack2，Rack3。</p>
<ol>
<li><p>Client将FileA按64M分块。分成两块，block1和Block2;</p>
</li>
<li><p>Client向nameNode发送写数据请求，如图<code>蓝色虚线</code>①——&gt;。</p>
</li>
<li><p>NameNode节点，记录block信息。并返回可用的DataNode，如<code>粉色虚线</code>②———&gt;。</p>
</li>
</ol>
<p>Block1: host2,host1,host3<br>Block2: host7,host8,host4</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;原理：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;NameNode具有RackAware机架感知功能，这个可以配置。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;若client为DataNode节点，那存储block时，规则为：副本1，同client的节点上；副本2，不同机架节点上；副本3，同第二个副本机架的另一个节点上；其他副本随机挑选。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;若client不为DataNode节点，那存储block时，规则为：副本1，随机选择一个节点上；副本2，不同副本1，机架上；副本3，同副本2相同的另一个节点上；其他副本随机挑选。</p>
<ol>
<li>client向DataNode发送block1；发送过程是以流式写入。</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;流式写入过程，</p>
<ul>
<li>1.将64M的block1按64k的package划分;</li>
<li>2.然后将第一个package发送给host2;</li>
<li>3.host2接收完后，将第一个package发送给host1，同时client想host2发送第二个package；</li>
<li>4.host1接收完第一个package后，发送给host3，同时接收host2发来的第二个package。</li>
<li>5.以此类推，如图红线实线所示，直到将block1发送完毕。</li>
<li>6.host2,host1,host3向NameNode，host2向Client发送通知，说“消息发送完了”。如图粉红颜色实线所示。</li>
<li>7.client收到host2发来的消息后，向namenode发送消息，说我写完了。这样就真完成了。如图黄色粗实线</li>
<li>8.发送完block1后，再向host7，host8，host4发送block2，如图蓝色实线所示。</li>
<li>9.发送完block2后，host7,host8,host4向NameNode，host7向Client发送通知，如图浅绿色实线所示。</li>
<li>10.client向NameNode发送消息，说我写完了，如图黄色粗实线。。。这样就完毕了。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>分析</strong>，通过写过程，我们可以了解到：</p>
<ul>
<li>写1T文件，我们需要3T的存储，3T的网络流量贷款。</li>
<li>在执行读或写的过程中，NameNode和DataNode通过HeartBeat进行保存通信，确定DataNode活着。如果发现DataNode死掉了，就将死掉的DataNode上的数据，放到其他节点去。读取时，要读其他节点去。</li>
<li>挂掉一个节点，没关系，还有其他节点可以备份；甚至，挂掉某一个机架，也没关系；其他机架上，也有备份。</li>
</ul>
<h2 id="读操作："><a href="#读操作：" class="headerlink" title="读操作："></a>读操作：</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E7%9A%84%E8%BF%90%E8%A1%8C%E5%8E%9F%E7%90%86/03.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;读操作就简单一些了，如图所示，client要从datanode上，读取FileA。而FileA由block1和block2组成。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;那么，读操作流程为：</p>
<ol>
<li><p>client向namenode发送读请求。</p>
</li>
<li><p>namenode查看Metadata信息，返回fileA的block的位置。<br>block1:host2,host1,host3<br>block2:host7,host8,host4    </p>
</li>
<li><p>block的位置是有先后顺序的，先读block1，再读block2。而且block1去host2上读取；然后block2，去host7上读取；</p>
</li>
</ol>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面例子中，client位于机架外，那么如果client位于机架内某个DataNode上，例如,client是host6。那么读取的时候，遵循的规律是：<br>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>优选读取本机架上的数据</strong>。</p>
<h2 id="HDFS中常用到的命令"><a href="#HDFS中常用到的命令" class="headerlink" title="HDFS中常用到的命令"></a>HDFS中常用到的命令</h2><h3 id="1、hadoop-fs"><a href="#1、hadoop-fs" class="headerlink" title="1、hadoop fs"></a>1、hadoop fs</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div></pre></td><td class="code"><pre><div class="line">hadoop fs -ls /  </div><div class="line">hadoop fs -lsr  </div><div class="line">hadoop fs -mkdir /user/hadoop  </div><div class="line">hadoop fs -put a.txt /user/hadoop/  </div><div class="line">hadoop fs -get /user/hadoop/a.txt /  </div><div class="line">hadoop fs -cp src dst  </div><div class="line">hadoop fs -mv src dst  </div><div class="line">hadoop fs -cat /user/hadoop/a.txt  </div><div class="line">hadoop fs -rm /user/hadoop/a.txt  </div><div class="line">hadoop fs -rmr /user/hadoop/a.txt  </div><div class="line">hadoop fs -text /user/hadoop/a.txt  </div><div class="line">hadoop fs -copyFromLocal localsrc dst <span class="comment">#与hadoop fs -put功能类似。  </span></div><div class="line">hadoop fs -moveFromLocal localsrc dst <span class="comment">#将本地文件上传到hdfs，同时删除本地文件。</span></div></pre></td></tr></table></figure>
<h3 id="2、hadoop-fsadmin"><a href="#2、hadoop-fsadmin" class="headerlink" title="2、hadoop fsadmin"></a>2、hadoop fsadmin</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">hadoop dfsadmin -report  </div><div class="line">hadoop dfsadmin -safemode enter | leave | get | <span class="built_in">wait</span>  </div><div class="line">hadoop dfsadmin -setBalancerBandwidth 1000</div></pre></td></tr></table></figure>
<h3 id="3、hadoop-fsck"><a href="#3、hadoop-fsck" class="headerlink" title="3、hadoop fsck"></a>3、hadoop fsck</h3><h3 id="4、start-balancer-sh"><a href="#4、start-balancer-sh" class="headerlink" title="4、start-balancer.sh"></a>4、start-balancer.sh</h3>
	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/6. HDFS原理分析—— 基本概念/">HDFS原理分析—— 基本概念</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS是Hadoop Distribute File System 的简称，也就是Hadoop的一个分布式文件系统。</p>
<h2 id="一、HDFS的主要设计理念"><a href="#一、HDFS的主要设计理念" class="headerlink" title="一、HDFS的主要设计理念"></a>一、HDFS的主要设计理念</h2><h3 id="1、存储超大文件"><a href="#1、存储超大文件" class="headerlink" title="1、存储超大文件"></a>1、存储超大文件</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这里的“超大文件”是指几百MB、GB甚至TB级别的文件。</p>
<h3 id="2、最高效的访问模式是-一次写入、多次读取-流式数据访问"><a href="#2、最高效的访问模式是-一次写入、多次读取-流式数据访问" class="headerlink" title="2、最高效的访问模式是 一次写入、多次读取(流式数据访问)"></a>2、最高效的访问模式是 一次写入、多次读取(流式数据访问)</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS存储的数据集作为hadoop的分析对象。在数据集生成后，长时间在此数据集上进行各种分析。每次分析都将设计该数据集的大部分数据甚至全部数据，因此读取整个数据集的时间延迟比读取第一条记录的时间延迟更重要。</p>
<h3 id="3、运行在普通廉价的服务器上"><a href="#3、运行在普通廉价的服务器上" class="headerlink" title="3、运行在普通廉价的服务器上"></a>3、运行在普通廉价的服务器上</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS设计理念之一就是让它能运行在普通的硬件之上，即便硬件出现故障，也可以通过容错策略来保证数据的高可用。</p>
<h2 id="二、HDFS的忌讳"><a href="#二、HDFS的忌讳" class="headerlink" title="二、HDFS的忌讳"></a>二、HDFS的忌讳</h2><h3 id="1、将HDFS用于对数据访问要求低延迟的场景"><a href="#1、将HDFS用于对数据访问要求低延迟的场景" class="headerlink" title="1、将HDFS用于对数据访问要求低延迟的场景"></a>1、将HDFS用于对数据访问要求低延迟的场景</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;由于HDFS是为高数据吞吐量应用而设计的，必然以高延迟为代价。</p>
<h3 id="2、存储大量小文件"><a href="#2、存储大量小文件" class="headerlink" title="2、存储大量小文件"></a>2、存储大量小文件</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS中元数据（文件的基本信息）存储在namenode的内存中，而namenode为单点，小文件数量大到一定程度，namenode内存就吃不消了。</p>
<h2 id="三、HDFS基本概念"><a href="#三、HDFS基本概念" class="headerlink" title="三、HDFS基本概念"></a>三、HDFS基本概念</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;数据块（block）：大文件会被分割成多个block进行存储，block大小默认为64MB。每一个block会在多个datanode上存储多份副本，默认是3份。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;namenode：namenode负责管理文件目录、文件和block的对应关系以及block和datanode的对应关系。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;datanode：datanode就负责存储了，当然大部分容错机制都是在datanode上实现的。</p>
<h2 id="四、HDFS基本架构图"><a href="#四、HDFS基本架构图" class="headerlink" title="四、HDFS基本架构图"></a>四、HDFS基本架构图</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E2%80%94%E2%80%94%20%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;图中有几个概念需要介绍一下</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Rack 是指机柜的意思，一个block的三个副本通常会保存到两个或者两个以上的机柜中（当然是机柜中的服务器），这样做的目的是做防灾容错，因为发生一个机柜掉电或者一个机柜的交换机挂了的概率还是蛮高的。</p>
<h2 id="五、HDFS写文件流程"><a href="#五、HDFS写文件流程" class="headerlink" title="五、HDFS写文件流程"></a>五、HDFS写文件流程</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E2%80%94%E2%80%94%20%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;思考：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在datanode执行create file后，namenode采用什么策略给client分配datanode？</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;顺序写入三个datanode，写入过程中有一个datanode挂掉了，如何容错？</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;client往datanode写入数据时挂掉了，怎么容错？</p>
<h2 id="六、HDFS读文件流程"><a href="#六、HDFS读文件流程" class="headerlink" title="六、HDFS读文件流程"></a>六、HDFS读文件流程</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%E5%8E%9F%E7%90%86%E5%88%86%E6%9E%90%E2%80%94%E2%80%94%20%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5/03.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;思考：namenode挂掉了怎么办，这个时候HDFS是否有相应的容错方案。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/5. HDFS 原理、架构与特性介绍/">HDFS 原理、架构与特性介绍</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;本文主要讲述 HDFS原理-架构、副本机制、HDFS负载均衡、机架感知、健壮性、文件删除恢复机制</p>
<h2 id="1-当前HDFS架构详尽分析"><a href="#1-当前HDFS架构详尽分析" class="headerlink" title="1.当前HDFS架构详尽分析"></a>1.当前HDFS架构详尽分析</h2><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/01.jpeg?raw=true" alt=""></figure></p>
<h3 id="HDFS架构"><a href="#HDFS架构" class="headerlink" title="HDFS架构"></a>HDFS架构</h3><ul>
<li>NameNode </li>
<li>DataNode </li>
<li>Sencondary NameNode </li>
</ul>
<h3 id="数据存储细节"><a href="#数据存储细节" class="headerlink" title="数据存储细节"></a>数据存储细节</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/02.jpeg?raw=true" alt=""></figure></p>
<h3 id="NameNode-目录结构"><a href="#NameNode-目录结构" class="headerlink" title="NameNode 目录结构"></a>NameNode 目录结构</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 的目录结构： </p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line"><span class="variable">$&#123; dfs.name.dir&#125;</span>/current /VERSION</div><div class="line">                         /edits</div><div class="line">                         /fsimage</div><div class="line">                         /fstime</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;dfs.name.dir 是 hdfs-site.xml 里配置的若干个目录组成的列表。 </p>
<h4 id="NameNode"><a href="#NameNode" class="headerlink" title="NameNode"></a>NameNode</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 上保存着 HDFS 的名字空间。对于任何对文件系统元数据产生修改的操作， Namenode 都会使用一种称为 EditLog 的事务日志记录下来。例如，在 HDFS 中创建一个文件， Namenode 就会在 Editlog 中插入一条记录来表示；同样地，修改文件的副本系数也将往 Editlog 插入一条记录。 Namenode 在本地操作系统的文件系统中存储这个 Editlog 。整个文件系统的名 字空间，包括数据块到文件的映射、文件的属性等，都存储在一个称为 FsImage 的文件中，这 个文件也是放在 Namenode 所在的本地文件系统上。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 在内存中保存着整个文件系统的名字空间和文件数据块映射 (Blockmap) 的映像 。这个关键的元数据结构设计得很紧凑，因而一个有 4G 内存的 Namenode 足够支撑大量的文件 和目录。当 Namenode 启动时，它从硬盘中读取 Editlog 和 FsImage ，将所有 Editlog 中的事务作 用在内存中的 FsImage 上，并将这个新版本的 FsImage 从内存中保存到本地磁盘上，然后删除 旧的 Editlog ，因为这个旧的 Editlog 的事务都已经作用在 FsImage 上了。这个过程称为一个检查 点 (checkpoint) 。在当前实现中，检查点只发生在 Namenode 启动时，在不久的将来将实现支持 周期性的检查点。 </p>
<h4 id="HDFS-NameSpace"><a href="#HDFS-NameSpace" class="headerlink" title="HDFS NameSpace"></a>HDFS NameSpace</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS 支持传统的层次型文件组织结构。用户或者应用程序可以创建目 录，然后将文件保存在这些目录里。文件系统名字空间的层次结构和大多数 现有的文件系统类似：用户可以创建、删除、移动或重命名文件。当前， HDFS 不支持用户磁盘配额和访问权限控制，也不支持硬链接和软链接。但 是 HDFS 架构并不妨碍实现这些特性。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 负责维护文件系统命名空间，任何对文件系统名字空间或属 性的修改都将被 Namenode 记录下来。应用程序可以设置 HDFS 保存的文件 的副本数目。文件副本的数目称为文件的副本系数，这个信息也是由 Namenode 保存的。 </p>
<h4 id="DataNode"><a href="#DataNode" class="headerlink" title="DataNode"></a>DataNode</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Datanode 将 HDFS 数据以文件的形式存储在本地的文件系统中，它并不知道有 关 HDFS 文件的信息。它把每个 HDFS 数据块存储在本地文件系统的一个单独的文件 中。 Datanode 并不在同一个目录创建所有的文件，实际上，它用试探的方法来确定 每个目录的最佳文件数目，并且在适当的时候创建子目录。在同一个目录中创建所 有的本地文件并不是最优的选择，这是因为本地文件系统可能无法高效地在单个目 录中支持大量的文件。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当一个 Datanode 启动时，它会扫描本地文件系统，产生一个这些本地文件对应 的所有 HDFS 数据块的列表，然后作为报告发送到 Namenode ，这个报告就是块状态 报告。</p>
<h4 id="配置Secondary-NameNode"><a href="#配置Secondary-NameNode" class="headerlink" title="配置Secondary NameNode"></a>配置Secondary NameNode</h4><ul>
<li>conf/masters文件指定的为Secondary NameNode节点</li>
<li>修改在masters文件中配置了的机器上的conf/hdfs-site.xml文件，加上如下选项：</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div></pre></td><td class="code"><pre><div class="line">&lt;property&gt;</div><div class="line">    &lt;name&gt;dfs.http.address&lt;/name&gt;</div><div class="line">    &lt;value&gt;namenode.hadoop-host.com:50070&lt;/value&gt;</div><div class="line">&lt;/property&gt;</div></pre></td></tr></table></figure>
<ul>
<li>core-site.xml：这里有2个参数可配置，但一般来说我们不做修改。fs.checkpoint.period表示多长时间记录一次hdfs的镜像。默认是1小时。fs.checkpoint.size表示一次记录多大的size，默认64M。</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">&lt;property&gt;</div><div class="line">    &lt;name&gt;fs.checkpoint.period&lt;/name&gt;</div><div class="line">    &lt;value&gt;3600&lt;/value&gt;</div><div class="line">    &lt;description&gt;The number of seconds between two periodic checkpoints. &lt;/description</div><div class="line">&lt;/property&gt;</div><div class="line">&lt;property&gt;</div><div class="line">    &lt;name&gt;fs.checkpoint.size&lt;/name&gt;</div><div class="line">    &lt;value&gt;67108864&lt;/value&gt;</div><div class="line">    &lt;description&gt;The size of the current edit <span class="built_in">log</span> (<span class="keyword">in</span> bytes) that triggers a periodic checkpoint even <span class="keyword">if</span> the fs.checkpoint.period hasn<span class="string">'t expired. &lt;/description&gt;</span></div><div class="line"><span class="string">&lt;/property&gt;</span></div></pre></td></tr></table></figure>
<h3 id="Secondary-NameNode"><a href="#Secondary-NameNode" class="headerlink" title="Secondary NameNode"></a>Secondary NameNode</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Secondary NameNode 定期合并 fsimage 和 edits 日志，将 edits 日志文件大小控制在一个限度下。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/03.jpeg?raw=true" alt=""></figure></p>
<h4 id="Secondary-NameNode处理流程"><a href="#Secondary-NameNode处理流程" class="headerlink" title="Secondary NameNode处理流程"></a>Secondary NameNode处理流程</h4><ol>
<li>namenode 响应 Secondary namenode 请求，将 edit log 推送给 Secondary namenode ， 开始重新写一个新的 edit log 。</li>
<li>Secondary namenode 收到来自 namenode 的 fsimage 文件和 edit log 。</li>
<li>Secondary namenode 将 fsimage 加载到内存，应用 edit log ， 并生成一 个新的 fsimage 文件。</li>
<li>Secondary namenode 将新的 fsimage 推送给 Namenode 。</li>
<li>Namenode 用新的 fsimage 取代旧的 fsimage ， 在 fstime 文件中记下检查 点发生的时</li>
</ol>
<h4 id="HDFS通信协议"><a href="#HDFS通信协议" class="headerlink" title="HDFS通信协议"></a>HDFS通信协议</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;所有的 HDFS 通讯协议都是构建在 TCP/IP 协议上。客户端通过一个可 配置的端口连接到 Namenode ， 通过 ClientProtocol 与 Namenode 交互。而 Datanode 是使用 DatanodeProtocol 与 Namenode 交互。再设计上， DataNode 通过周期性的向 NameNode 发送心跳和数据块来保持和 NameNode 的通信，数据块报告的信息包括数据块的属性，即数据块属于哪 个文件，数据块 ID ，修改时间等， NameNode 的 DataNode 和数据块的映射 关系就是通过系统启动时 DataNode 的数据块报告建立的。从 ClientProtocol 和 Datanodeprotocol 抽象出一个远程调用 ( RPC ）， 在设计上， Namenode 不会主动发起 RPC ， 而是是响应来自客户端和 Datanode 的 RPC 请求。 </p>
<h4 id="HDFS的安全模式"><a href="#HDFS的安全模式" class="headerlink" title="HDFS的安全模式"></a>HDFS的安全模式</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 启动后会进入一个称为安全模式的特殊状态。处于安全模式 的 Namenode 是不会进行数据块的复制的。 Namenode 从所有的 Datanode 接收心跳信号和块状态报告。块状态报告包括了某个 Datanode 所有的数据 块列表。每个数据块都有一个指定的最小副本数。当 Namenode 检测确认某 个数据块的副本数目达到这个最小值，那么该数据块就会被认为是副本安全 (safely replicated) 的；在一定百分比（这个参数可配置）的数据块被 Namenode 检测确认是安全之后（加上一个额外的 30 秒等待时间）， Namenode 将退出安全模式状态。接下来它会确定还有哪些数据块的副本没 有达到指定数目，并将这些数据块复制到其他 Datanode 上。</p>
<h2 id="2-HDFS文件读取的解析"><a href="#2-HDFS文件读取的解析" class="headerlink" title="2.HDFS文件读取的解析"></a>2.HDFS文件读取的解析</h2><p>###文件读取流程</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/04.jpeg?raw=true" alt=""></figure></p>
<h3 id="流程分析"><a href="#流程分析" class="headerlink" title="流程分析"></a>流程分析</h3><ul>
<li>使用HDFS提供的客户端开发库Client，向远程的Namenode发起RPC请求；</li>
<li>Namenode会视情况返回文件的部分或者全部block列表，对于每个block，Namenode都会返回有该block拷贝的DataNode地址；</li>
<li>客户端开发库Client会选取离客户端最接近的DataNode来读取block；如果客户端本身就是DataNode,那么将从本地直接获取数据.</li>
<li>读取完当前block的数据后，关闭与当前的DataNode连接，并为读取下一个block寻找最佳的DataNode；</li>
<li>当读完列表的block后，且文件读取还没有结束，客户端开发库会继续向Namenode获取下一批的block列表。</li>
<li>读取完一个block都会进行checksum验证，如果读取datanode时出现错误，客户端会通知Namenode，然后再从下一个拥有该block拷贝的datanode继续读。</li>
</ul>
<h2 id="3-HDFS文件写入的解析"><a href="#3-HDFS文件写入的解析" class="headerlink" title="3.HDFS文件写入的解析"></a>3.HDFS文件写入的解析</h2><h3 id="文件写入流程"><a href="#文件写入流程" class="headerlink" title="文件写入流程"></a>文件写入流程</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/05.jpeg?raw=true" alt=""></figure></p>
<h3 id="流程分析-1"><a href="#流程分析-1" class="headerlink" title="流程分析"></a>流程分析</h3><ul>
<li>使用HDFS提供的客户端开发库Client，向远程的Namenode发起RPC请求； </li>
<li>Namenode会检查要创建的文件是否已经存在，创建者是否有权限进行操作，成功则会为文件 创建一个记录，否则会让客户端抛出异常；</li>
<li>当客户端开始写入文件的时候，会将文件切分成多个packets，并在内部以数据队列”data queue”的形式管理这些packets，并向Namenode申请新的blocks，获取用来存储replicas的合适的datanodes列表，列表的大小根据在Namenode中对replication的设置而定。</li>
<li>开始以pipeline（管道）的形式将packet写入所有的replicas中。把packet以流的方式写入第一个datanode，该datanode把该packet存储之后，再将其传递给在此pipeline中的下一个datanode，直到最后一个datanode，这种写数据的方式呈流水线的形式。</li>
<li>最后一个datanode成功存储之后会返回一个ack packet，在pipeline里传递至客户端，在客户端的开发库内部维护着”ack queue”，成功收到datanode返回的ack packet后会从”ack queue”移除相应的packet。</li>
<li>如果传输过程中，有某个datanode出现了故障，那么当前的pipeline会被关闭，出现故障的datanode会从当前的pipeline中移除，剩余的block会继续剩下的datanode中继续以pipeline的形式传输，同时Namenode会分配一个新的datanode，保持replicas设定的数量。</li>
</ul>
<h3 id="流水线复制"><a href="#流水线复制" class="headerlink" title="流水线复制"></a>流水线复制</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当客户端向 HDFS 文件写入数据的时候，一开始是写到本地临时文件中。假设该文件的副 本系数设置为 3 ，当本地临时文件累积到一个数据块的大小时，客户端会从 Namenode 获取一个 Datanode 列表用于存放副本。然后客户端开始向第一个 Datanode 传输数据，第一个 Datanode 一小部分一小部分 (4 KB) 地接收数据，将每一部分写入本地仓库，并同时传输该部分到列表中 第二个 Datanode 节点。第二个 Datanode 也是这样，一小部分一小部分地接收数据，写入本地 仓库，并同时传给第三个 Datanode 。最后，第三个 Datanode 接收数据并存储在本地。因此， Datanode 能流水线式地从前一个节点接收数据，并在同时转发给下一个节点，数据以流水线的 方式从前一个 Datanode 复制到下一个</p>
<h3 id="更细节的原理"><a href="#更细节的原理" class="headerlink" title="更细节的原理"></a>更细节的原理</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;客户端创建文件的请求其实并没有立即发送给 Namenode ，事实上，在刚开始阶 段 HDFS 客户端会先将文件数据缓存到本地的一个临时文件。应用程序的写操作被透 明地重定向到这个临时文件。当这个临时文件累积的数据量超过一个数据块的大小 ，客户端才会联系 Namenode 。 Namenode 将文件名插入文件系统的层次结构中，并 且分配一个数据块给它。然后返回 Datanode 的标识符和目标数据块给客户端。接着 客户端将这块数据从本地临时文件上传到指定的 Datanode 上。当文件关闭时，在临 时文件中剩余的没有上传的数据也会传输到指定的 Datanode 上。然后客户端告诉 Namenode 文件已经关闭。此时 Namenode 才将文件创建操作提交到日志里进行存储 。如果 Namenode 在文件关闭前宕机了，则该文件将丢失。</p>
<h2 id="4-副本机制"><a href="#4-副本机制" class="headerlink" title="4.副本机制"></a>4.副本机制</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;特点</p>
<ol>
<li>数据类型单一</li>
<li>副本数比较多</li>
<li>写文件时副本的放置方法</li>
<li>动态的副本创建策略</li>
<li>弱化的副本一致性要求</li>
</ol>
<h3 id="副本摆放策略"><a href="#副本摆放策略" class="headerlink" title="副本摆放策略"></a>副本摆放策略</h3><p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/06.jpeg?raw=true" alt=""></figure></p>
<h3 id="修改副本数"><a href="#修改副本数" class="headerlink" title="修改副本数"></a>修改副本数</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;集群只有三个Datanode，hadoop系统replication=4时，会出现什么情况？</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对于上传文件到hdfs上时，当时hadoop的副本系数是几，这个文件的块数副本数就会有几份，无论以后你怎么更改系统副本系统，这个文件的副本数都不会改变，也就说上传到分布式系统上的文件副本数由当时的系统副本数决定，不会受replication的更改而变化，除非用命令来更改文件的副本数。因为dfs.replication实质上是client参数，在create文件时可以指定具体replication，属性dfs.replication是不指定具体replication时的采用默认备份数。文件上传后，备份数已定，修改dfs.replication是不会影响以前的文件的，也不会影响后面指定备份数的文件。只影响后面采用默认备份数的文件。但可以利用hadoop提供的命令后期改某文件的备份数：hadoop fs -setrep -R 1。如果你是在hdfs-site.xml设置了dfs.replication，这并一定就得了，因为你可能没把conf文件夹加入到你的 project的classpath里，你的程序运行时取的dfs.replication可能是hdfs-default.xml里的 dfs.replication，默认是3。可能这个就是造成你为什么dfs.replication老是3的原因。你可以试试在创建文件时，显式设定replication。replication一般到3就可以了，大了意义也不大。</p>
<h2 id="5-HDFS负载均衡"><a href="#5-HDFS负载均衡" class="headerlink" title="5.HDFS负载均衡"></a>5.HDFS负载均衡</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS的数据也许并不是非常均匀的分布在各个DataNode中。一个常见的原因是在现有的集群上经常会增添新的DataNode节点。当新增一个数据块（一个文件的数据被保存在一系列的块中）时，NameNode在选择DataNode接收这个数据块之前，会考虑到很多因素。其中的一些考虑的是：</p>
<ul>
<li>将数据块的一个副本放在正在写这个数据块的节点上。</li>
<li>尽量将数据块的不同副本分布在不同的机架上，这样集群可在完全失去某一机架的情况下还能存活。</li>
<li>一个副本通常被放置在和写文件的节点同一机架的某个节点上，这样可以减少跨越机架的网络I/O。</li>
<li>尽量均匀地将HDFS数据分布在集群的DataNode中。</li>
</ul>
<h2 id="6-HDFS机架感知"><a href="#6-HDFS机架感知" class="headerlink" title="6.HDFS机架感知"></a>6.HDFS机架感知</h2><h3 id="HDFS机架感知"><a href="#HDFS机架感知" class="headerlink" title="HDFS机架感知"></a>HDFS机架感知</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通常，大型 Hadoop 集群是以机架的形式来组织的，同一个机架上不同 节点间的网络状况比不同机架之间的更为理想。 另外， NameNode 设法将 数据块副本保存在不同的机架上以提高容错性。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;而 HDFS 不能够自动判断集群中各个 datanode 的网络拓扑情况 Hadoop 允 许集群的管理员通过配置 dfs.network.script 参数来确定节点所处的机架。 文 件提供了 IP-&gt;rackid 的翻译。 NameNode 通过这个得到集群中各个 datanode 机器的 rackid 。 如果 topology.script.file.name 没有设定，则每个 IP 都会翻译 成 / default-rack。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/HDFS%20%E5%8E%9F%E7%90%86%E3%80%81%E6%9E%B6%E6%9E%84%E4%B8%8E%E7%89%B9%E6%80%A7%E4%BB%8B%E7%BB%8D/07.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;有了机架感知， NameNode 就可以画出上图所示的 datanode 网络拓扑图。 D1,R1 都是交换机，最底层是 datanode 。 则 H1 的 rackid=/D1/R1/H1 ， H1 的 parent 是 R1 ， R1 的是 D1 。 这些 rackid 信息可以通过 topology.script.file.name 配置。有了这些 rackid 信息就可以计算出任意两台 datanode 之间的距离。</p>
<p>distance(/D1/R1/H1,/D1/R1/H1)=0  相同的 datanode<br>distance(/D1/R1/H1,/D1/R1/H2)=2  同一 rack 下的不同 datanode<br>distance(/D1/R1/H1,/D1/R1/H4)=4  同一 IDC 下的不同 datanode<br>distance(/D1/R1/H1,/D2/R3/H7)=6   不同 IDC 下的 datanode    </p>
<h2 id="7-HDFS访问"><a href="#7-HDFS访问" class="headerlink" title="7.HDFS访问"></a>7.HDFS访问</h2><h3 id="访问方式"><a href="#访问方式" class="headerlink" title="访问方式"></a>访问方式</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160; HDFS 给应用提供了多种访问方式。用户可以通过 Java API 接口访问，也 可以通过 C 语言的封装 API 访问，还可以通过浏览器的方式访问 HDFS 中的文件。    </p>
<h2 id="8-HDFS-健壮性"><a href="#8-HDFS-健壮性" class="headerlink" title="8.HDFS 健壮性"></a>8.HDFS 健壮性</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;HDFS 的主要目标就是即使在出错的情况下也要保证数据存储的可靠性。 常见的三种出错情况是： Namenode 出错 , Datanode 出错和网络割裂 ( network partitions) 。   </p>
<h3 id="磁盘数据错误，心跳检测和重新复制"><a href="#磁盘数据错误，心跳检测和重新复制" class="headerlink" title="磁盘数据错误，心跳检测和重新复制"></a>磁盘数据错误，心跳检测和重新复制</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;每个 Datanode 节点周期性地向 Namenode 发送心跳信号。网络割裂可能 导致一部分 Datanode 跟 Namenode 失去联系。 Namenode 通过心跳信号的缺 失来检测这一情况，并将这些近期不再发送心跳信号 Datanode 标记为宕机 ，不会再将新的 IO 请求发给它们。任何存储在宕机 Datanode 上的数据将不 再有效。 Datanode 的宕机可能会引起一些数据块的副本系数低于指定值， Namenode 不断地检测这些需要复制的数据块，一旦发现就启动复制操作。 在下列情况下，可能需要重新复制：某个 Datanode 节点失效，某个副本遭 到损坏， Datanode 上的硬盘错误，或者文件的副本系数增大。    </p>
<h3 id="数据完整性"><a href="#数据完整性" class="headerlink" title="数据完整性"></a>数据完整性</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;从某个 Datanode 获取的数据块有可能是损坏的，损坏可能是由 Datanode 的存储设备错误、网络错误或者软件 bug 造成的。 HDFS 客户端软 件实现了对 HDFS 文件内容的校验和 (checksum) 检查。当客户端创建一个新 的 HDFS 文件，会计算这个文件每个数据块的校验和，并将校验和作为一个 单独的隐藏文件保存在同一个 HDFS 名字空间下。当客户端获取文件内容后 ，它会检验从 Datanode 获取的数据跟相应的校验和文件中的校验和是否匹 配，如果不匹配，客户端可以选择从其他 Datanode 获取该数据块的副本。    </p>
<h3 id="元数据磁盘错误"><a href="#元数据磁盘错误" class="headerlink" title="元数据磁盘错误"></a>元数据磁盘错误</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;FsImage 和 Editlog 是 HDFS 的核心数据结构。如果这些文件损坏了，整个 HDFS 实例都将失效。因而， Namenode 可以配置成支持维护多个 FsImage 和 Editlog 的副本。任何对 FsImage 或者 Editlog 的修改，都将同步到它们的副 本上。这种多副本的同步操作可能会降低 Namenode 每秒处理的名字空间事 务数量。然而这个代价是可以接受的，因为即使 HDFS 的应用是数据密集的 ，它们也非元数据密集的。当 Namenode 重启的时候，它会选取最近的完整 的 FsImage 和 Editlog 来使用。    </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Namenode 是 HDFS 集群中的单点故障 (single point of failure) 所在。如果 Namenode 机器故障，是需要手工干预的。目前，自动重启或在另一台机器 上做 Namenode 故障转移的功能还没实现。    </p>
<h3 id="快照"><a href="#快照" class="headerlink" title="快照"></a>快照</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;快照支持某一特定时刻的数据的复制备份。利用快照，可以让 HDFS 在 数据损坏时恢复到过去一个已知正确的时间点。 HDFS 目前还不支持快照功 能，但计划在将来的版本进行支持。     </p>
<h2 id="9-HDFS-文件删除恢复机制"><a href="#9-HDFS-文件删除恢复机制" class="headerlink" title="9.HDFS 文件删除恢复机制"></a>9.HDFS 文件删除恢复机制</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当用户或应用程序删除某个文件时，这个文件并没有立刻从 HDFS 中删 除。实际上， HDFS 会将这个文件重命名转移到 /trash 目录。只要文件还在 /trash 目录中，该文件就可以被迅速地恢复。文件在 /trash 中保存的时间是可 配置的，当超过这个时间时， Namenode 就会将该文件从名字空间中删除。 删除文件会使得该文件相关的数据块被释放。注意，从用户删除文件到 HDFS 空闲空间的增加之间会有一定时间的延迟。    </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;只要被删除的文件还在 /trash 目录中，用户就可以恢复这个文件。如果 用户想恢复被删除的文件，他 / 她可以浏览 /trash 目录找回该文件。 /trash 目 录仅仅保存被删除文件的最后副本。 /trash 目录与其他的目录没有什么区别 ，除了一点：在该目录上 HDFS 会应用一个特殊策略来自动删除文件。目前 的默认策略是删除 /trash 中保留时间超过 6 小时的文件。将来，这个策略可以 通过一个被良好定义的接口配置。    </p>
<h3 id="开启回收站"><a href="#开启回收站" class="headerlink" title="开启回收站"></a>开启回收站</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div></pre></td><td class="code"><pre><div class="line">hdfs-site.xml    </div><div class="line">&lt;configuration&gt;    </div><div class="line">       &lt;property&gt;    </div><div class="line">               &lt;name&gt;fs.trash.interval&lt;/name&gt;    </div><div class="line">                &lt;value&gt;    1440   &lt;/value&gt;    </div><div class="line">                &lt;description&gt;Number ofminutes between trash checkpoints.    </div><div class="line">                        If zero, the trashfeature is disabled.    </div><div class="line">                &lt;/description&gt;    </div><div class="line">       &lt;/property&gt;    </div><div class="line">&lt;/configuration&gt;</div></pre></td></tr></table></figure>
<ol>
<li>fs.trash.interval参数设置保留时间为 1440 分钟(1天)    </li>
<li>回收站的位置：在HDFS上的 /user/$USER/.Trash/Current/      </li>
</ol>
<h2 id="10-HDFS-分布式缓存（DistributedCache-）"><a href="#10-HDFS-分布式缓存（DistributedCache-）" class="headerlink" title="10.HDFS 分布式缓存（DistributedCache ）"></a>10.HDFS 分布式缓存（DistributedCache ）</h2><ol>
<li>在HDFS上准备好要共享的数据(text、archive、jar)，你拼路径的时候必须加前缀”file://“说明是本地路径，否则hadoop默认访问的路径是hdfs。</li>
<li>DistributedCache 在 Mapper 或者 Reducer 启动时会被 copy to local，然后被 DistributedCache.getLocalCacheFiles() 调用，运行完 job 后 local cache file 会被删掉，如果另一个 job 也需要这样一份文件，需要重新添加、重新缓存，因为在分布式场景下 task 并不知道该 node 是否存在 cache file。如果在同台机器已经有了dist cache file,不会再次download，DistributedCache 根据缓存文档修改的时间戳进行追踪。 在作业执行期间，当前应用程序或者外部程序不能修改缓存文件，所以分布式缓存一般用来缓存只读文件。</li>
<li>DistributedCache 在添加的时候注意要添加具体的文件，如果你添加目录，DistributedCache 将不会自动遍历、识别目录下的文件。</li>
</ol>
<h2 id="11-HDFS缺点"><a href="#11-HDFS缺点" class="headerlink" title="11.HDFS缺点"></a>11.HDFS缺点</h2><h3 id="大量小文件"><a href="#大量小文件" class="headerlink" title="大量小文件"></a>大量小文件</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;因为 Namenode 把文件系统的元数据放置在内存中，所以文件系统所能 容纳的文件数目是由 Namenode 的内存大小来决定。一般来说，每一个文件 、文件夹和 Block 需要占据 150 字节左右的空间，所以，如果你有 100 万个文 件，每一个占据一个 Block ，你就至少需要 300MB 内存。当前来说，数百万 的文件还是可行的，当扩展到数十亿时，对于当前的硬件水平来说就没法实 现了。还有一个问题就是，因为 Map task 的数量是由 splits 来决定的，所以 用 MR 处理大量的小文件时，就会产生过多的 Maptask ，线程管理开销将会 增加作业时间。举个例子，处理 10000M 的文件，若每个 split 为 1M ，那就会 有 10000 个 Maptasks ，会有很大的线程开销；若每个 split 为 100M ，则只有 100 个 Maptasks ，每个 Maptask 将会有更多的事情做，而线程的管理开销也 将减小很多。 </p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/4. Avro简介/">Avro简介</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<h2 id="一、引言"><a href="#一、引言" class="headerlink" title="一、引言"></a>一、引言</h2><h3 id="1、-简介"><a href="#1、-简介" class="headerlink" title="1、 简介"></a>1、 简介</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro是Hadoop中的一个子项目，也是Apache中一个独立的项目，Avro是一个基于二进制数据传输高性能的中间件。在Hadoop的其他项目中例如HBase(Ref)和Hive(Ref)的Client端与服务端的数据传输也采用了这个工具。Avro是一个数据序列化的系统。Avro 可以将数据结构或对象转化成便于存储或传输的格式。Avro设计之初就用来支持数据密集型应用，适合于远程或本地大规模数据的存储和交换。</p>
<h3 id="2、-特点"><a href="#2、-特点" class="headerlink" title="2、 特点"></a>2、 特点</h3><ul>
<li>丰富的数据结构类型；</li>
<li>快速可压缩的二进制数据形式，对数据二进制序列化后可以节约数据存储空间和网络传输带宽；</li>
<li>存储持久数据的文件容器；</li>
<li>可以实现远程过程调用RPC；</li>
<li>简单的动态语言结合功能。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;avro支持跨编程语言实现（C, C++, C#，Java, Python, Ruby, PHP），类似于Thrift，但是avro的显著特征是：avro依赖于模式，动态加载相关数据的模式，Avro数据的读写操作很频繁，而这些操作使用的都是模式，这样就减少写入每个数据文件的开销，使得序列化快速而又轻巧。这种数据及其模式的自我描述方便了动态脚本语言的使用。当Avro数据存储到文件中时，它的模式也随之存储，这样任何程序都可以对文件进行处理。如果读取数据时使用的模式与写入数据时使用的模式不同，也很容易解决，因为读取和写入的模式都是已知的。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/01.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro和动态语言结合后，读/写数据文件和使用RPC协议都不需要生成代码，而代码生成作为一种可选的优化只需要在静态类型语言中实现。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro依赖于模式(Schema)。通过模式定义各种数据结构，只有确定了模式才能对数据进行解释，所以在数据的序列化和反序列化之前，必须先确定模式的结构。正是模式的引入，使得数据具有了自描述的功能，同时能够实现动态加载，另外与其他的数据序列化系统如Thrift相比，数据之间不存在其他的任何标识，有利于提高数据处理的效率。    </p>
<h2 id="二、技术要领"><a href="#二、技术要领" class="headerlink" title="二、技术要领"></a>二、技术要领</h2><h3 id="1、类型"><a href="#1、类型" class="headerlink" title="1、类型"></a>1、类型</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;数据类型标准化的意义：一方面使不同系统对相同的数据能够正确解析，另一方面，数据类型的标准定义有利于数据序列化/反序列化。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;简单的数据类型：Avro定义了几种简单数据类型，下表是其简单说明：</p>
<table>
<thead>
<tr>
<th>类型</th>
<th>说明</th>
</tr>
</thead>
<tbody>
<tr>
<td>null</td>
<td>no value</td>
</tr>
<tr>
<td>boolean</td>
<td>a binary value</td>
</tr>
<tr>
<td>int</td>
<td>32-bit signed integer</td>
</tr>
<tr>
<td>long</td>
<td>64-bit signed integer</td>
</tr>
<tr>
<td>float</td>
<td>single precision (32-bit) IEEE 754 floating-point number</td>
</tr>
<tr>
<td>double</td>
<td>double precision (64-bit) IEEE 754 floating-point number</td>
</tr>
<tr>
<td>bytes</td>
<td>sequence of 8-bit unsigned bytes</td>
</tr>
<tr>
<td>string</td>
<td>unicode character sequence</td>
</tr>
</tbody>
</table>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;简单数据类型由类型名称定义，不包含属性信息，例如字符串定义如下：</p>
<p>{“type”: “string”}</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;复杂数据类型：Avro定义了六种复杂数据类型，每一种复杂数据类型都具有独特的属性，下表就每一种复杂数据类型进行说明。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/02.png?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;每一种复杂数据类型都含有各自的一些属性，其中部分属性是必需的，部分是可选的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这里需要说明Record类型中field属性的默认值，当Record Schema实例数据中某个field属性没有提供实例数据时，则由默认值提供，具体值见下表。Union的field默认值由Union定义中的第一个Schema决定。</p>
<table>
<thead>
<tr>
<th>avro type</th>
<th>json type</th>
<th>example</th>
</tr>
</thead>
<tbody>
<tr>
<td>null</td>
<td>null</td>
<td>null</td>
</tr>
<tr>
<td>boolean</td>
<td>boolean</td>
<td>true</td>
</tr>
<tr>
<td>int,long</td>
<td>integer</td>
<td>1</td>
</tr>
<tr>
<td>float,double</td>
<td>number</td>
<td>1.1</td>
</tr>
<tr>
<td>bytes</td>
<td>string</td>
<td>“\u00FF”</td>
</tr>
<tr>
<td>string</td>
<td>string</td>
<td>“foo”</td>
</tr>
<tr>
<td>record</td>
<td>object</td>
<td>{“a”: 1}</td>
</tr>
<tr>
<td>enum</td>
<td>string</td>
<td>“FOO”</td>
</tr>
<tr>
<td>array</td>
<td>array</td>
<td>[1]</td>
</tr>
<tr>
<td>map</td>
<td>object</td>
<td>{“a”: 1}</td>
</tr>
<tr>
<td>fixed</td>
<td>string</td>
<td>“\u00ff”</td>
</tr>
</tbody>
</table>
<h3 id="2、序列化-反序列化"><a href="#2、序列化-反序列化" class="headerlink" title="2、序列化/反序列化"></a>2、序列化/反序列化</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro指定两种数据序列化编码方式：binary encoding 和Json encoding。使用二进制编码会高效序列化，并且序列化后得到的结果会比较小；而JSON一般用于调试系统或是基于WEB的应用。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;binary encoding规则如下：</p>
<ol>
<li>简单数据类型</li>
</ol>
<table>
<thead>
<tr>
<th>Type</th>
<th>Encoding</th>
<th>Example</th>
</tr>
</thead>
<tbody>
<tr>
<td>null</td>
<td>Zero bytes</td>
<td>Null</td>
</tr>
<tr>
<td>boolean</td>
<td>A single byte</td>
<td>{true:1, false:0}</td>
</tr>
<tr>
<td>int/long</td>
<td>variable-length zig-zag coding</td>
<td>&#160;</td>
</tr>
<tr>
<td>float</td>
<td>4 bytes</td>
<td>Java’s floatToIntBits</td>
</tr>
<tr>
<td>double</td>
<td>8 bytes</td>
<td>Java’s doubleToLongBits</td>
</tr>
<tr>
<td>bytes</td>
<td>a long followed by that many bytes of data</td>
<td>&#160;</td>
</tr>
<tr>
<td>string</td>
<td>a long followed by that many bytes of UTF-8 encoded character data</td>
<td>“foo”:{3,f,o,o} 06 66 6f 6f</td>
</tr>
</tbody>
</table>
<ol>
<li>复杂数据类型</li>
</ol>
<table>
<thead>
<tr>
<th>Type</th>
<th>encoding</th>
</tr>
</thead>
<tbody>
<tr>
<td>Records</td>
<td>encoded just the concatenation of the encodings of its fields</td>
</tr>
<tr>
<td>Enums</td>
<td>a int representing the zero-based position of the symbol in the schema</td>
</tr>
<tr>
<td>Arrays</td>
<td>encoded as series of blocks. A block with count 0 indicates the end of the array. block:{long,items}</td>
</tr>
<tr>
<td>Maps</td>
<td>encoded as series of blocks. A block with count 0 indicates the end of the map. block:{long,key/value pairs}.</td>
</tr>
<tr>
<td>Unions</td>
<td>encoded by first writing a long value indicating the zero-based position within the union of the schema of its value. The value is then encoded per the indicated schema within the union.</td>
</tr>
<tr>
<td>fixed</td>
<td>encoded using number of bytes declared in the schema</td>
</tr>
</tbody>
</table>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;实例：</p>
<ul>
<li>records</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line"><span class="string">"type"</span>:<span class="string">"record"</span>,</div><div class="line"><span class="string">"name"</span>:<span class="string">"test"</span>,</div><div class="line"><span class="string">"fields"</span> : [</div><div class="line">&#123;<span class="string">"name"</span>: <span class="string">"a"</span>,<span class="string">"type"</span>: <span class="string">"long"</span>&#125;,</div><div class="line">&#123;<span class="string">"name"</span>: <span class="string">"b"</span>,<span class="string">"type"</span>: <span class="string">"string"</span>&#125;</div><div class="line">]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;假设：a=27b=”foo” (encoding：36(27), 06(3), 66(“f”), 6f(“o”))</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;binary encoding：3606 66 6f 6f</p>
<ul>
<li>enums</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div></pre></td><td class="code"><pre><div class="line">&#123;<span class="string">"type"</span>: <span class="string">"enum"</span>,<span class="string">"name"</span>: <span class="string">"Foo"</span>, <span class="string">"symbols"</span>: [<span class="string">"A"</span>,<span class="string">"B"</span>, <span class="string">"C"</span>, <span class="string">"D"</span>] &#125;</div><div class="line">“D”(encoding: 06(3))</div><div class="line">binary encoding: 06</div></pre></td></tr></table></figure>
<ul>
<li>arrays</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">&#123;<span class="string">"type"</span>: <span class="string">"array"</span>,<span class="string">"items"</span>: <span class="string">"long"</span>&#125;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;设：{3, 27 } (encoding：04(2), 06(3), 36(27) )</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;binary encoding：0406 36 00</p>
<ul>
<li>maps</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;设：{(“a”:1), (“b”:2) } （encoding：61(“a”), 62(“b”), 02(1), 04(2)）</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;binary encoding：0261 02 02 62 04</p>
<ul>
<li>unions</li>
</ul>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">[<span class="string">"string"</span>,<span class="string">"null"</span>]</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;设：(1)null; (2) “a”<br>binary encoding：</p>
<p>(1) 02；说明：02代表null在union定义中的位置1；</p>
<p>(2) 00 02 61；说明：00为string在union定义的位置，02 61为”a”的编码。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;图1表示的是Avro本地序列化和反序列化的实例，它将用户定义的模式和具体的数据编码成二进制序列存储在对象容器文件中，例如用户定义了包含学号、姓名、院系和电话的学生模式，而Avro对其进行编码后存储在student.db文件中，其中存储数据的模式放在文件头的元数据中，这样读取的模式即使与写入的模式不同，也可以迅速地读出数据。假如另一个程序需要获取学生的姓名和电话，只需要定义包含姓名和电话的学生模式，然后用此模式去读取容器文件中的数据即可。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/06.jpeg?raw=true" alt=""></figure></p>
<h3 id="3、模式Schema"><a href="#3、模式Schema" class="headerlink" title="3、模式Schema"></a>3、模式Schema</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Schema通过JSON对象表示。Schema定义了简单数据类型和复杂数据类型，其中复杂数据类型包含不同属性。通过各种数据类型用户可以自定义丰富的数据结构。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Schema由下列JSON对象之一定义：</p>
<ul>
<li>JSON字符串：命名</li>
<li>JSON对象：{“type”: “typeName” …attributes…}</li>
<li>JSON数组：Avro中Union的定义</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;举例：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div></pre></td><td class="code"><pre><div class="line">&#123;<span class="string">"namespace"</span>: <span class="string">"example.avro"</span>,</div><div class="line"> <span class="string">"type"</span>:<span class="string">"record"</span>,</div><div class="line"> <span class="string">"name"</span>:<span class="string">"User"</span>,</div><div class="line"> <span class="string">"fields"</span>: [</div><div class="line">     &#123;<span class="string">"name"</span>:<span class="string">"name"</span>, <span class="string">"type"</span>: <span class="string">"string"</span>&#125;,</div><div class="line">     &#123;<span class="string">"name"</span>:<span class="string">"favorite_number"</span>, <span class="string">"type"</span>: [<span class="string">"int"</span>, <span class="string">"null"</span>]&#125;,</div><div class="line">     &#123;<span class="string">"name"</span>:<span class="string">"favorite_color"</span>, <span class="string">"type"</span>: [<span class="string">"string"</span>,<span class="string">"null"</span>]&#125;</div><div class="line"> ]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="4、排序"><a href="#4、排序" class="headerlink" title="4、排序"></a>4、排序</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro为数据定义了一个标准的排列顺序。比较在很多时候是经常被使用到的对象之间的操作，标准定义可以进行方便有效的比较和排序。同时标准的定义可以方便对Avro的二进制编码数据直接进行排序而不需要反序列化。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;只有当数据项包含相同的Schema的时候，数据之间的比较才有意义。数据的比较按照Schema深度优先，从左至右的顺序递归的进行。找到第一个不匹配即可终止比较。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;两个拥有相同的模式的项的比较按照以下规则进行：</p>
<ul>
<li>null：总是相等。</li>
<li>int,long,float：按照数值大小比较。</li>
<li>boolean：false在true之前。</li>
<li>string：按照字典序进行比较。</li>
<li>bytes，fixed：按照byte的字典序进行比较。</li>
<li>array：按照元素的字典序进行比较。</li>
<li>enum：按照符号在枚举中的位置比较。</li>
<li>record：按照域的字典序排序，如果指定了以下属性：</li>
<li>“ascending”，域值的顺序不变。</li>
<li>“descending”，域值的顺序颠倒。</li>
<li>“ignore”，排序的时候忽略域值。</li>
<li>map：不可进行比较。</li>
</ul>
<h3 id="5、对象容器文件"><a href="#5、对象容器文件" class="headerlink" title="5、对象容器文件"></a>5、对象容器文件</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro定义了一个简单的对象容器文件格式。一个文件对应一个模式，所有存储在文件中的对象都是根据模式写入的。对象按照块进行存储，块可以采用压缩的方式存储。为了在进行mapreduce处理的时候有效的切分文件，在块之间采用了同步记号。一个文件可以包含任意用户定义的元数据。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一个文件由两部分组成：文件头和一个或者多个文件数据块。</p>
<ul>
<li>文件头：<ul>
<li>四个字节，ASCII‘O’，‘b’，‘j’,1。</li>
<li>文件元数据，用于描述Schema。</li>
<li>16字节的文件同步记号。</li>
<li>其中，文件元数据的格式为：<ul>
<li>i.             值为-1的长整型，表明这是一个元数据块。</li>
<li>ii.             标识块长度的长整型。</li>
<li>iii.             标识块中key/value对数目的长整型。</li>
<li>iv.             每一个key/value对的string key和bytesvalue。</li>
<li>v.             标识块中字节总数的4字节长的整数。</li>
</ul>
</li>
</ul>
</li>
<li>文件数据块：<br>数据是以块结构进行组织的，一个文件可以包含一个或者多个文件数据块。<ul>
<li>表示文件中块中对象数目的长整型。</li>
<li>表示块中数据序列化后的字节数长度的长整型。</li>
<li>序列化的对象。</li>
<li>16字节的文件同步记号。</li>
</ul>
</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当数据块的长度为0时即为文件数据块的最后一个数据，此后的所有数据被自动忽略。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;下图示对象容器文件的结构分解及说明：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/03.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一个存储文件由两部分组成:头信息(Header)和数据块(Data Block)。而头信息又由三部分构成：四个字节的前缀，文件Meta-data信息和随机生成的16字节同步标记符。Avro目前支持的Meta-data有两种：schema和codec。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;codec表示对后面的文件数据块(File Data Block)采用何种压缩方式。Avro的实现都需要支持下面两种压缩方式：null(不压缩)和deflate(使用Deflate算法压缩数据块)。除了文档中认定的两种Meta-data，用户还可以自定义适用于自己的Meta-data。这里用long型来表示有多少个Meta-data数据对，也是让用户在实际应用中可以定义足够的Meta-data信息。对于每对Meta-data信息，都有一个string型的key(需要以“avro.” 为前缀)和二进制编码后的value。对于文件中头信息之后的每个数据块，有这样的结构：一个long值记录当前块有多少个对象，一个long值用于记录当前块经过压缩后的字节数，真正的序列化对象和16字节长度的同步标记符。由于对象可以组织成不同的块，使用时就可以不经过反序列化而对某个数据块进行操作。还可以由数据块数，对象数和同步标记符来定位损坏的块以确保数据完整性。</p>
<h2 id="三、RPC实现"><a href="#三、RPC实现" class="headerlink" title="三、RPC实现"></a>三、RPC实现</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当在RPC中使用Avro时，服务器和客户端可以在握手连接时交换模式。服务器和客户端有彼此全部的模式，因此相同命名字段、缺失字段和多余字段等信息之间通信中需要处理的一致性问题就可以容易解决。如图2所示，协议中定义了用于传输的消息，消息使用框架后放入缓冲区中进行传输，由于传输的初始就交换了各自的协议定义，因此即使传输双方使用的协议不同所传输的数据也能够正确解析。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/04.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Avro作为RPC框架来使用。客户端希望同服务器端交互时，就需要交换双方通信的协议，它类似于模式，需要双方来定义，在Avro中被称为消息(Message)。通信双方都必须保持这种协议，以便于解析从对方发送过来的数据，这也就是传说中的握手阶段。 </p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;消息从客户端发送到服务器端需要经过传输层(Transport Layer)，它发送消息并接收服务器端的响应。到达传输层的数据就是二进制数据。通常以HTTP作为传输模型，数据以POST方式发送到对方去。在 Avro中，它的消息被封装成为一组缓冲区(Buffer)，类似于下图的模型：</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Avro%E7%AE%80%E4%BB%8B/05.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如上图，每个缓冲区以四个字节开头，中间是多个字节的缓冲数据，最后以一个空缓冲区结尾。这种机制的好处在于，发送端在发送数据时可以很方便地组装不同数据源的数据，接收方也可以将数据存入不同的存储区。还有，当往缓冲区中写数据时，大对象可以独占一个缓冲区，而不是与其它小对象混合存放，便于接收方方便地读取大对象。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对象容器文件是Avro的数据存储的具体实现，数据交换则由RPC服务提供，与对象容器文件类似，数据交换也完全依赖Schema，所以与Hadoop目前的RPC不同，Avro在数据交换之前需要通过握手过程先交换Schema。</p>
<h3 id="1、握手过程"><a href="#1、握手过程" class="headerlink" title="1、握手过程"></a>1、握手过程</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;握手的过程是确保Server和Client获得对方的Schema定义，从而使Server能够正确反序列化请求信息，Client能够正确反序列化响应信息。一般的，Server/Client会缓存最近使用到的一些协议格式，所以，大多数情况下，握手过程不需要交换整个Schema文本。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;所有的RPC请求和响应处理都建立在已经完成握手的基础上。对于无状态的连接，所有的请求响应之前都附有一次握手过程；对于有状态的连接，一次握手完成，整个连接的生命期内都有效。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;具体过程：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Client发起HandshakeRequest，其中含有Client本身SchemaHash值和对应Server端的Schema Hash值(clientHash!=null,clientProtocol=null, serverHash!=null)。如果本地缓存有serverHash值则直接填充，如果没有则通过猜测填充。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Server用如下之一HandshakeResponse响应Client请求：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(match=BOTH, serverProtocol=null,serverHash=null)：当Client发送正确的serverHash值且Server缓存相应的clientHash。握手过程完成，之后的数据交换都遵守本次握手结果。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(match=CLIENT, serverProtocol!=null,serverHash!=null)：当Server缓存有Client的Schema，但是Client请求中ServerHash值不正确。此时Server发送Server端的Schema数据和相应的Hash值，此次握手完成，之后的数据交换都遵守本次握手结果。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;(match=NONE)：当Client发送的ServerHash不正确且Server端没有Client Schema的缓存。这种情况下Client需要重新提交请求信息 (clientHash!=null,clientProtocol!=null, serverHash!=null)，Server响应 (match=BOTH, serverProtocol=null,serverHash=null)，此次握手过程完成，之后的数据交换都遵守本次握手结果。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;握手过程使用的Schema结构如下示。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div></pre></td><td class="code"><pre><div class="line">&#123;</div><div class="line"><span class="string">"type"</span>:<span class="string">"record"</span>,</div><div class="line"><span class="string">"name"</span>:<span class="string">"HandshakeRequest"</span>,<span class="string">"namespace"</span>:<span class="string">"org.apache.avro.ipc"</span>,</div><div class="line"><span class="string">"fields"</span>:[</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"clientHash"</span>, <span class="string">"type"</span>: &#123;<span class="string">"type"</span>: <span class="string">"fixed"</span>,<span class="string">"name"</span>: <span class="string">"MD5"</span>, <span class="string">"size"</span>: 16&#125;&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"clientProtocol"</span>, <span class="string">"type"</span>: [<span class="string">"null"</span>,<span class="string">"string"</span>]&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"serverHash"</span>, <span class="string">"type"</span>: <span class="string">"MD5"</span>&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"meta"</span>, <span class="string">"type"</span>: [<span class="string">"null"</span>, &#123;<span class="string">"type"</span>:<span class="string">"map"</span>, <span class="string">"values"</span>: <span class="string">"bytes"</span>&#125;]&#125;</div><div class="line">]</div><div class="line">&#125;</div><div class="line">&#123;</div><div class="line"><span class="string">"type"</span>:<span class="string">"record"</span>,</div><div class="line"><span class="string">"name"</span>:<span class="string">"HandshakeResponse"</span>, <span class="string">"namespace"</span>:<span class="string">"org.apache.avro.ipc"</span>,</div><div class="line"><span class="string">"fields"</span>:[</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"match"</span>,<span class="string">"type"</span>: &#123;<span class="string">"type"</span>: <span class="string">"enum"</span>,<span class="string">"name"</span>: <span class="string">"HandshakeMatch"</span>,</div><div class="line"><span class="string">"symbols"</span>:[<span class="string">"BOTH"</span>, <span class="string">"CLIENT"</span>, <span class="string">"NONE"</span>]&#125;&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"serverProtocol"</span>, <span class="string">"type"</span>: [<span class="string">"null"</span>,<span class="string">"string"</span>]&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"serverHash"</span>,<span class="string">"type"</span>: [<span class="string">"null"</span>, &#123;<span class="string">"type"</span>:<span class="string">"fixed"</span>, <span class="string">"name"</span>: <span class="string">"MD5"</span>, <span class="string">"size"</span>: 16&#125;]&#125;,</div><div class="line">&#123;<span class="string">"name"</span>:<span class="string">"meta"</span>,<span class="string">"type"</span>: [<span class="string">"null"</span>, &#123;<span class="string">"type"</span>:<span class="string">"map"</span>, <span class="string">"values"</span>: <span class="string">"bytes"</span>&#125;]&#125;</div><div class="line">]</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="2、消息帧格式"><a href="#2、消息帧格式" class="headerlink" title="2、消息帧格式"></a>2、消息帧格式</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;消息从客户端发送到服务器端需要经过传输层，它发送请求并接收服务器端的响应。到达传输层的数据就是二进制数据。通常以HTTP作为传输模型，数据以POST方式发送到对方去。在 Avro中消息首先分帧后被封装成为一组缓冲区(Buffer)。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;数据帧的格式如下：</p>
<ul>
<li>一系列Buffer：<ul>
<li>4字节的Buffer长度</li>
<li>Buffer字节数据</li>
</ul>
</li>
<li>长度为0的Buffer结束数据帧 </li>
</ul>
<h3 id="3、Call格式"><a href="#3、Call格式" class="headerlink" title="3、Call格式"></a>3、Call格式</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;一个调用由请求消息、结果响应消息或者错误消息组成。请求和响应包含可扩展的元数据，两种消息都按照之前提出的方法分帧。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;调用的请求格式为：</p>
<ul>
<li>请求元数据，一个类型值的映射。</li>
<li>消息名，一个Avro字符串。</li>
<li>消息参数。参数根据消息的请求定义序列化。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;调用的响应格式为：</p>
<ul>
<li>响应的元数据，一个类型值的映射。</li>
<li>一字节的错误标志位。</li>
<li>如果错误标志为false，响应消息，根据响应的模式序列化。</li>
</ul>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果错误标志位true，错误消息，根据消息的错误联合模式序列化。 </p>
<h2 id="四、实例"><a href="#四、实例" class="headerlink" title="四、实例"></a>四、实例</h2><h3 id="1、本地序列化-反序列化"><a href="#1、本地序列化-反序列化" class="headerlink" title="1、本地序列化/反序列化"></a>1、本地序列化/反序列化</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div></pre></td><td class="code"><pre><div class="line">user.avsc</div><div class="line">&#123;<span class="string">"namespace"</span>:<span class="string">"example.avro"</span>,</div><div class="line"> <span class="string">"type"</span>: <span class="string">"record"</span>,</div><div class="line"> <span class="string">"name"</span>: <span class="string">"User"</span>,</div><div class="line"> <span class="string">"fields"</span>: [</div><div class="line">    &#123;<span class="string">"name"</span>: <span class="string">"name"</span>, <span class="string">"type"</span>:<span class="string">"string"</span>&#125;,</div><div class="line">    &#123;<span class="string">"name"</span>: <span class="string">"favorite_number"</span>,  <span class="string">"type"</span>: [<span class="string">"int"</span>, <span class="string">"null"</span>]&#125;,</div><div class="line">    &#123;<span class="string">"name"</span>: <span class="string">"favorite_color"</span>, <span class="string">"type"</span>:[<span class="string">"string"</span>, <span class="string">"null"</span>]&#125;</div><div class="line"> ]</div><div class="line">&#125;</div><div class="line">Main.java</div><div class="line">public class Main &#123;</div><div class="line">    public static void main(String[] args)throws Exception &#123;</div><div class="line">       User user1 = new User();</div><div class="line">       user1.setName(<span class="string">"Alyssa"</span>);</div><div class="line">       user1.setFavoriteNumber(256);</div><div class="line">       // Leave favorite color null </div><div class="line">       // Alternate constructor</div><div class="line">       User user2 = new User(<span class="string">"Ben"</span>, 7,<span class="string">"red"</span>); </div><div class="line">       // Construct via builder</div><div class="line">       User user3 = User.newBuilder()</div><div class="line">                    .setName(<span class="string">"Charlie"</span>)</div><div class="line">                   .setFavoriteColor(<span class="string">"blue"</span>)</div><div class="line">                   .setFavoriteNumber(null)</div><div class="line">                    .build();      </div><div class="line">       // Serialize user1 and user2to disk</div><div class="line">       File file = new File(<span class="string">"users.avro"</span>);</div><div class="line">       DatumWriter&lt;User&gt; userDatumWriter = new SpecificDatumWriter&lt;User&gt;(User.class);</div><div class="line">       DataFileWriter&lt;User&gt; dataFileWriter = newDataFileWriter&lt;User&gt;(userDatumWriter);</div><div class="line">       dataFileWriter.create(user1.getSchema(),new File(<span class="string">"users.avro"</span>));</div><div class="line">       dataFileWriter.append(user1);</div><div class="line">       dataFileWriter.append(user2);</div><div class="line">       dataFileWriter.append(user3);</div><div class="line">       dataFileWriter.close();      </div><div class="line">       // Deserialize Usersfrom disk</div><div class="line">       DatumReader&lt;User&gt; userDatumReader = newSpecificDatumReader&lt;User&gt;(User.class);</div><div class="line">       DataFileReader&lt;User&gt; dataFileReader = newDataFileReader&lt;User&gt;(file, userDatumReader);</div><div class="line">       User user = null;</div><div class="line">       <span class="keyword">while</span> (dataFileReader.hasNext()) &#123;</div><div class="line">       // Reuse user object bypassing it to next(). This saves us from</div><div class="line">       // allocating and garbagecollecting many objects <span class="keyword">for</span> files with</div><div class="line">       // many items.</div><div class="line">       user = dataFileReader.next(user);</div><div class="line">       System.out.println(user);</div><div class="line">       &#125;                  </div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>
<h3 id="2、RPC"><a href="#2、RPC" class="headerlink" title="2、RPC"></a>2、RPC</h3><figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div><div class="line">42</div><div class="line">43</div><div class="line">44</div><div class="line">45</div><div class="line">46</div><div class="line">47</div><div class="line">48</div><div class="line">49</div><div class="line">50</div><div class="line">51</div><div class="line">52</div><div class="line">53</div><div class="line">54</div><div class="line">55</div><div class="line">56</div></pre></td><td class="code"><pre><div class="line">mail.avsc</div><div class="line">&#123;<span class="string">"namespace"</span>:<span class="string">"example.proto"</span>,</div><div class="line"> <span class="string">"protocol"</span>: <span class="string">"Mail"</span>,</div><div class="line"> <span class="string">"types"</span>: [</div><div class="line">    &#123;<span class="string">"name"</span>: <span class="string">"Message"</span>, <span class="string">"type"</span>:<span class="string">"record"</span>,</div><div class="line">     <span class="string">"fields"</span>: [</div><div class="line">         &#123;<span class="string">"name"</span>: <span class="string">"to"</span>,  <span class="string">"type"</span>: <span class="string">"string"</span>&#125;,</div><div class="line">         &#123;<span class="string">"name"</span>: <span class="string">"from"</span>, <span class="string">"type"</span>: <span class="string">"string"</span>&#125;,</div><div class="line">         &#123;<span class="string">"name"</span>: <span class="string">"body"</span>, <span class="string">"type"</span>:<span class="string">"string"</span>&#125;</div><div class="line">     ]</div><div class="line">    &#125;</div><div class="line"> ],</div><div class="line"> <span class="string">"messages"</span>: &#123;</div><div class="line">    <span class="string">"send"</span>: &#123;</div><div class="line">        <span class="string">"request"</span>: [&#123;<span class="string">"name"</span>: <span class="string">"message"</span>,<span class="string">"type"</span>: <span class="string">"Message"</span>&#125;],</div><div class="line">        <span class="string">"response"</span>: <span class="string">"string"</span></div><div class="line">    &#125;</div><div class="line"> &#125;</div><div class="line">&#125;</div><div class="line">Main.java</div><div class="line">public class Main &#123;</div><div class="line">    public static class MailImpl implements Mail &#123;</div><div class="line">        // <span class="keyword">in</span> this simple example just <span class="built_in">return</span> details of the message</div><div class="line">        public Utf8 send(Message message) &#123;</div><div class="line">            System.out.println(<span class="string">"Sending message"</span>);</div><div class="line">            <span class="built_in">return</span> new Utf8(<span class="string">"Sending message to "</span> + message.getTo().toString()</div><div class="line">                    + <span class="string">" from "</span> +message.getFrom().toString()</div><div class="line">                    + <span class="string">" with body "</span> +message.getBody().toString());</div><div class="line">        &#125;</div><div class="line">    &#125;</div><div class="line">    private static Server server;</div><div class="line">    private static void startServer() throws IOException &#123;</div><div class="line">        server = new NettyServer(new SpecificResponder(Mail.class,new MailImpl()),newInetSocketAddress(65111));</div><div class="line">        // the server implements the Mail protocol (MailImpl)</div><div class="line">    &#125;</div><div class="line">    public static void main(String[] args)throws IOException &#123;</div><div class="line">        System.out.println(<span class="string">"Starting server"</span>);</div><div class="line">        // usually this would be anotherapp, but <span class="keyword">for</span> simplicity</div><div class="line">        startServer();</div><div class="line">        System.out.println(<span class="string">"Server started"</span>);</div><div class="line">        NettyTransceiver client = new NettyTransceiver(new InetSocketAddress(65111));</div><div class="line">        // client code - attach to the server and send a message</div><div class="line">        Mail proxy = (Mail) SpecificRequestor.getClient(Mail.class, client);</div><div class="line">        System.out.println(<span class="string">"Client built, got proxy"</span>);</div><div class="line">        // fill <span class="keyword">in</span> the Message record and send it</div><div class="line">        Message message = new Message();</div><div class="line">        message.setTo(new Utf8(<span class="string">"127.0.0.1"</span>));</div><div class="line">        message.setFrom(new Utf8(<span class="string">"127.0.0.1"</span>));</div><div class="line">        message.setBody(new Utf8(<span class="string">"this is my message"</span>));</div><div class="line">        System.out.println(<span class="string">"Calling proxy.send with message: "</span> + message.toString());</div><div class="line">        System.out.println(<span class="string">"Result: "</span> +proxy.send(message));</div><div class="line">        // cleanup</div><div class="line">        client.close();</div><div class="line">        server.close();</div><div class="line">    &#125;</div><div class="line">&#125;</div></pre></td></tr></table></figure>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/3. Ambari——大数据平台的搭建利器/">Ambari——大数据平台的搭建利器</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;虽然大数据越来越流行，但其学习的门槛却一直阻碍着很多的分布式应用初学者或者大数据的业务应用开发者。多个产品之间的不兼容问题，快速集成和维护也显得比较困难。不管是 Hadoop V1 或者 V2 的安装，又或者 Spark/YARN 等的集成，都不是几行简单的命令可以完成的，而是需要手工修改很多的集群配置，这进一步增加了业务开发者的学习和使用难度。有了 Ambari，这些都不再是难题。</p>
<h2 id="Ambari-是什么"><a href="#Ambari-是什么" class="headerlink" title="Ambari 是什么"></a>Ambari 是什么</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari 跟 Hadoop 等开源软件一样，也是 Apache Software Foundation 中的一个项目，并且是顶级项目。目前最新的发布版本是 2.0.1，未来不久将发布 2.1 版本。就 Ambari 的作用来说，就是创建、管理、监视 Hadoop 的集群，但是这里的 Hadoop 是广义，指的是 Hadoop 整个生态圈（例如 Hive，Hbase，Sqoop，Zookeeper 等），而并不仅是特指 Hadoop。用一句话来说，Ambari 就是为了让 Hadoop 以及相关的大数据软件更容易使用的一个工具。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;说到这里，大家就应该明白什么人最需要 Ambari 了。那些苦苦花费好几天去安装、调试 Hadoop 的初学者是最能体会到 Ambari 的方便之处的。而且，Ambari 现在所支持的平台组件也越来越多，例如流行的 Spark，Storm 等计算框架，以及资源调度平台 YARN 等，我们都能轻松地通过 Ambari 来进行部署。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari 自身也是一个分布式架构的软件，主要由两部分组成：Ambari Server 和 Ambari Agent。简单来说，用户通过 Ambari Server 通知 Ambari Agent 安装对应的软件；Agent 会定时地发送各个机器每个软件模块的状态给 Ambari Server，最终这些状态信息会呈现在 Ambari 的 GUI，方便用户了解到集群的各种状态，并进行相应的维护。详细的操作和介绍会在后续章节介绍。</p>
<h2 id="Ambari-的安装"><a href="#Ambari-的安装" class="headerlink" title="Ambari 的安装"></a>Ambari 的安装</h2><h3 id="安装准备"><a href="#安装准备" class="headerlink" title="安装准备"></a>安装准备</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;关于 Ambari 的安装，目前网上能找到两个发行版，一个是 Apache 的 Ambari，另一个是 Hortonworks 的，两者区别不大。这里就以 Apache 的 Ambari 2.0.1 作为示例。本文使用三台 Redhat 6.6 作为安装环境（目前测试验证结果为 Ambari 在 Redhat 6.6 的版本上运行比较稳定），三台机器分别为 zwshen37.example.com、zwshen38.example.com、zwshen39.example.com。zwshen37 计划安装为 Ambari 的 Server，另外两台为 Ambari Agent。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;安装 Ambari 最方便的方式就是使用公共的库源（public repository）。有兴趣的朋友可以自己研究一下搭建一个本地库（local repository）进行安装。这个不是重点，所以不在此赘述。在进行具体的安装之前，需要做几个准备工作。</p>
<h4 id="1-SSH-的无密码登录；"><a href="#1-SSH-的无密码登录；" class="headerlink" title="1. SSH 的无密码登录；"></a>1. SSH 的无密码登录；</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari 的 Server 会 SSH 到 Agent 的机器，拷贝并执行一些命令。因此我们需要配置 Ambari Server 到 Agent 的 SSH 无密码登录。在这个例子里，zwshen37 可以 SSH 无密码登录 zwshen38 和 zwshen39。</p>
<h4 id="2-确保-Yum-可以正常工作；"><a href="#2-确保-Yum-可以正常工作；" class="headerlink" title="2. 确保 Yum 可以正常工作；"></a>2. 确保 Yum 可以正常工作；</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;通过公共库（public repository），安装 Hadoop 这些软件，背后其实就是应用 Yum 在安装公共库里面的 rpm 包。所以这里需要您的机器都能访问 Internet。</p>
<h4 id="3-确保-home-目录的写权限。"><a href="#3-确保-home-目录的写权限。" class="headerlink" title="3. 确保 home 目录的写权限。"></a>3. 确保 home 目录的写权限。</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari 会创建一些 OS 用户。</p>
<h4 id="4-确保机器的-Python-版本大于或等于-2-6-（Redhat6-6，默认就是-2-6-的）。"><a href="#4-确保机器的-Python-版本大于或等于-2-6-（Redhat6-6，默认就是-2-6-的）。" class="headerlink" title="4. 确保机器的 Python 版本大于或等于 2.6.（Redhat6.6，默认就是 2.6 的）。"></a>4. 确保机器的 Python 版本大于或等于 2.6.（Redhat6.6，默认就是 2.6 的）。</h4><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;以上的准备工作完成后，便可以真正的开始安装 Ambari 了。</p>
<h3 id="安装过程"><a href="#安装过程" class="headerlink" title="安装过程"></a>安装过程</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先需要获取 Ambari 的公共库文件（public repository）。登录到 Linux 主机并执行下面的命令（也可以自己手工下载）：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">wget http://public-repo-1.hortonworks.com/ambari/centos6/2.x/updates/2.0.1/ambari.repo</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;将下载的 ambari.repo 文件拷贝到 Linux 的系统目录/etc/yum.repos.d/。拷贝完后，我们需要获取该公共库的所有的源文件列表。依次执行以下命令。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div></pre></td><td class="code"><pre><div class="line">yum clean all</div><div class="line">yum list|grep ambari</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如图 1 所示：</p>
<p><strong>图 1. 获取公共库源文件列表</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/01.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果可以看到 Ambari 的对应版本的安装包列表，说明公共库已配置成功。然后就可以安装 Ambari 的 package 了。执行下面的命令安装 Ambari Server 到该机器。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">yum install ambari-server</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;待安装完成后，便需要对 Ambari Server 做一个简单的配置。执行下面的命令。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">amari-server setup</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在这个交互式的设置中，采用默认配置即可。Ambari 会使用 Postgres 数据库，默认会安装并使用 Oracle 的 JDK。默认设置了 Ambari GUI 的登录用户为 admin/admin。并且指定 Ambari Server 的运行用户为 root。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;简单的 setup 配置完成后。就可以启动 Ambari 了。运行下面的命令。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ambari-server start</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当成功启动 Ambari Server 之后，便可以从浏览器登录，默认的端口为 8080。以本文环境为例，在浏览器的地址栏输入 <a href="http://zwshen37.example.com:8080，登录密码为" target="_blank" rel="external">http://zwshen37.example.com:8080，登录密码为</a> admin/admin。登入 Ambari 之后的页面如下图。</p>
<p><strong>图 2. Ambari 的 welcome 页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/02.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;至此，Ambari Server 就安装完成了。</p>
<h2 id="部署一个-Hadoop2-x-集群"><a href="#部署一个-Hadoop2-x-集群" class="headerlink" title="部署一个 Hadoop2.x 集群"></a>部署一个 Hadoop2.x 集群</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;到这一节，我们将可以真正地体验到 Ambari 的用武之地，以及它所能带来的方便之处。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;登录 Ambari 之后，点击按钮“Launch Install Wizard”，就可以开始创建属于自己的大数据平台。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第一步</strong>，命名集群的名字。本环境为 bigdata。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第二步</strong>，选择一个 Stack，这个 Stack 相当于一个 Hadoop 生态圈软件的集合。Stack 的版本越高，里面的软件版本也就越高。这里我们选择 HDP2.2，里面的对应的 Hadoop 版本为 2.6.x。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第三步</strong>，指定 Agent 机器（如果配置了域，必须包含完整域名，例如本文环境的域为 example.com），这些机器会被安装 Hadoop 等软件包。还记得在安装章节中提到的 SSH 无密码登陆吗，这里需要指定当时在 Ambari Server 机器生成的私钥（ssh-keygen 生成的，公钥已经拷贝到 Ambari Agent 的机器，具体的 SSH 无密码登录配置，可以在网上很容易找到配置方法，不在此赘述）。另外不要选择“Perform manual registration on hosts and do not use SSH“。因为我们需要 Ambari Server 自动去安装 Ambari Agent。具体参见下图示例。</p>
<p><strong>图 3. 安装配置页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/03.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第四步</strong>，Ambari Server 会自动安装 Ambari Agent 到刚才指定的机器列表。安装完成后，Agent 会向 Ambari Server 注册。成功注册后，就可以继续 Next 到下一步。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第五步</strong>，这里我们终于看到跟 Hadoop 有关的名词了。在这一步，我们需要选择要安装的软件名称。本文环境选择了 HDFS，YARN + MapReduce2，Zoopkeeper，Storm 以及 Spark。选的越多，就会需要越多的机器内存。选择之后就可以继续下一步了。这里需要注意某些 Service 是有依赖关系的。如果您选了一个需要依赖其他 Service 的一个 Service，Ambari 会提醒安装对应依赖的 Service。参见下图。</p>
<p><strong>图 4. Service 选择页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/04.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第六步和第七步</strong>，分别是选择安装软件所指定的 Master 机器和 Slave 机器，以及 Client 机器。这里使用默认选择即可（真正在生产环境中，需要根据具体的机器配置选择）。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第八步</strong>，就是 Service 的配置。绝大部分配置已经有默认值，不需要修改。初学者，如果不需要进行调优是可以直接使用默认配置的。有些 Service 会有一些必须的手工配置项，则必须手动输入，才可以下一步。本文环境直接使用默认配置。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第九步</strong>，Ambari 会总结一个安装列表，供用户审阅。这里没问题，就直接下一步。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;<strong>第十步</strong>，Ambari 会开始安装选择的 Service 到 Ambari Agent 的机器（如下图）。这里可能需要等好一会，因为都是在线安装。安装完成之后，Ambari 就会启动这些 Service。</p>
<p><strong>图 5. Service 的安装进度</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/05.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;安装完成之后，就可以查看 Ambari 的 Dashboard 了。例如下图。</p>
<p><strong>图 6. Ambari 的 Dashboard 页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/06.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;至此，您专属的 bigdata 集群已经安装完成。</p>
<h2 id="利用-Ambari-管理-Hadoop-集群"><a href="#利用-Ambari-管理-Hadoop-集群" class="headerlink" title="利用 Ambari 管理 Hadoop 集群"></a>利用 Ambari 管理 Hadoop 集群</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;在上一章节中，我们已经体验到 Ambari 为 Hadoop 生态圈的安装提供的便利。这已经省去了很多的人力成本。尤其是对大数据圈子的测试人员来说，自动化就容易了很多。下面我们看看如何通过 Ambari 管理 Hadoop 的集群。</p>
<h3 id="Service-Level-Action（服务级别的操作）"><a href="#Service-Level-Action（服务级别的操作）" class="headerlink" title="Service Level Action（服务级别的操作）"></a>Service Level Action（服务级别的操作）</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先我们进到 Ambari 的 GUI 页面，并查看 Dashboard。在左侧的 Service 列表中，我们可以点击任何一个您想要操作的 Service。以 MapReduce2 为例（Hadoop 这里的版本为 2.6.x，也就是 YARN+HDFS+MapReduce），当点击 MapReduce2 后，就会看到该 Service 的相关信息，如下图。</p>
<p><strong>图 7. MapRduce2 的 Service 页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/07.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;中间部分是 Service 的模块（Component）信息，也就是该 Service 有哪些模块及其数目。右上角有个 Service Action 的按钮，当点击该按钮后就可以看到很多 Service 的控制命令。也就是通过这些 Service Action 命令，对 Service 进行管理的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;可能有的人会说，装完 Hadoop 的集群后，并不知道这个集群是不是可用。这时候我们就可以运行一个“Run Service Check”。点击这个命令后，就会出现下图的进度显示。</p>
<p><strong>图 8. MapReduce Service Check</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/08.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;其实这里就是通过运行一个经典的 MapReduce Wordcount 实例，来检查 MapReduce 是不是正常。对于 Service Action 里面的 Start、Stop 的含义就是，启停整个集群所有该 Service 的模块（也就是 Service level）。当执行进度页面弹出来的时候，我们可以点击 Operations 的名字，进而查看每个机器的进度和运行 log。如下图 Stop 的操作。</p>
<p><strong>图 9. 命令执行进度 1</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/09.jpeg?raw=true" alt=""></figure></p>
<p><strong>图 10. 命令执行进度 2</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/10.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;维护模式（Maintenance Mode）以及如何添加一个自定义的命令到 Service Action，我会在后续的连载中进行介绍。</p>
<h3 id="Host-Level-Action（机器级别的操作）"><a href="#Host-Level-Action（机器级别的操作）" class="headerlink" title="Host Level Action（机器级别的操作）"></a>Host Level Action（机器级别的操作）</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先，我们回到 Ambari 的 Dashboard 页面。页面最上面中间的地方有个 Hosts，点击这个标签，我们就可以看到 Ambari 所管理的机器列表。如下图。</p>
<p><strong>图 11. Ambari 的机器列表</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/11.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;图片中红色的数字是警告信息（Ambari Alert），这里我们先略过它，后续文章再做介绍。先看左上角的 Actions，点击这个按钮，就可以看到 Host level Action 的选项了，其实和 Service Level 是类似的，只是执行的范围不一样。如下图。当用户选择 All Hosts -&gt; Hosts -&gt; Start All Components，Ambari 就会将所有 Service 的所有模块启动。</p>
<p><strong>图 12. 启动所有 Service 的所有模块</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/12.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果用户选择 All Hosts-&gt; DataNodes -&gt; Stop，Ambari 就会把所有机器的 DataNode 这个模块关闭。如下图。</p>
<p><strong>图 13. 关闭所有的 DataNode 模块</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/13.jpeg?raw=true" alt=""></figure></p>
<h3 id="Component-Level-Action（模块级别的操作）"><a href="#Component-Level-Action（模块级别的操作）" class="headerlink" title="Component Level Action（模块级别的操作）"></a>Component Level Action（模块级别的操作）</h3><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;上面的图中，我们可以看到 Decommisson、Recommission。这些命令其实是自定义的模块级别的操作（Component Level Action）。不过上图中命令一旦执行，就是对多个机器的同个模块执行。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们现在尝试只对单个机器的一个模块（Component）执行。首先我们回到 Hosts 的页面。这时候点击机器名，我们就会进入到该机器的 Component 页面。如下图。</p>
<p><strong>图 14. Component 页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/14.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这时候只要点击每个 Component（模块）后面的按钮，就可以看到该模块的操作命令了。例如，我们可以停掉这台机器的 DataNode 模块。</p>
<p><strong>图 15. 停止 DataNode 模块 1</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/15.jpeg?raw=true" alt=""></figure></p>
<p><strong>图 16. 停止 DataNode 模块 2</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/16.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;关于如何给一个模块添加自定义的命令，也会在后续的连载中做介绍。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这一章节中，主要介绍了如何通过三种级别的 Action（操作）管理 Hadoop 的集群。在 Ambari 中已经加入了很多自定义的 Action 去做一些特殊的操作。如果对 Hadoop 生态圈的软件足够熟悉，就可以尝试更多的 Action。可能有的人会问，Ambari 可不可以扩容集群。答案当然是可以的。Ambari 可以给自身的集群添加机器（也就是添加 Ambari Agent），然后将 Service 的模块安装在新的机器，也可以把某些模块安装到已有的其他的机器。篇幅有限，将在后续的连载中介绍更多的内容。</p>
<h2 id="Ambari-的架构和工作原理"><a href="#Ambari-的架构和工作原理" class="headerlink" title="Ambari 的架构和工作原理"></a>Ambari 的架构和工作原理</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari 基本的架构和工作原理如下图 17 所示。</p>
<p><strong>图 17. Ambari 的基本架构</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/17.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari Server 会读取 Stack 和 Service 的配置文件。当用 Ambari 创建集群的时候，Ambari Server 传送 Stack 和 Service 的配置文件以及 Service 生命周期的控制脚本到 Ambari Agent。Agent 拿到配置文件后，会下载安装公共源里软件包（Redhat，就是使用 yum 服务）。安装完成后，Ambari Server 会通知 Agent 去启动 Service。之后 Ambari Server 会定期发送命令到 Agent 检查 Service 的状态，Agent 上报给 Server，并呈现在 Ambari 的 GUI 上。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Ambari Server 支持 Rest API，这样可以很容易的扩展和定制化 Ambari。甚至于不用登陆 Ambari 的 GUI，只需要在命令行通过 curl 就可以控制 Ambari，以及控制 Hadoop 的 cluster。具体的 API 可以参见 Apache Ambari 的官方网页 API reference。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;对于安全方面要求比较苛刻的环境来说，Ambari 可以支持 Kerberos 认证的 Hadoop 集群。</p>
<h2 id="扩展-Ambari-管理一个自定义的-Service"><a href="#扩展-Ambari-管理一个自定义的-Service" class="headerlink" title="扩展 Ambari 管理一个自定义的 Service"></a>扩展 Ambari 管理一个自定义的 Service</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先，我们需要规划自定义的 Service 属于哪个 Stack（当然 Stack 也是可以自定义的）。这里为了快速创建一个新的 Service，而且我们已经安装了 HDP 2.2 的 Stack，所以就将自定义的 Service 放在 HDP 2.2 之下。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第一步，首先在 Ambari Service 机器上找到 HDP 2.2 Stack 的目录，如下图所示。</p>
<p><strong>图 18. HDP 2.2 的目录</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/18.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第二步，需要创建一个 Service 目录，我们这里用“SAMPLE”作为目录名。并在 SAMPLE 底下创建 metainfo.xml。示例代码如下。主要解释下 xml 代码中的两个字段 category 和 cardinality。category 指定了该模块（Component）的类别，可以是 MASTER、SLAVE、CLIENT。Cardinality 指的是所要安装的机器数，可以是固定数字 1，可以是一个范围比如 1-2，也可以是 1+，或者 ALL。如果是一个范围的时候，安装的时候会让用户选择机器。另外这里有关 Service 和 Component 的 name 配置要用大写，小写有时候会有问题。Displayname 可以随意设置。</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div><div class="line">25</div><div class="line">26</div><div class="line">27</div><div class="line">28</div><div class="line">29</div><div class="line">30</div><div class="line">31</div><div class="line">32</div><div class="line">33</div><div class="line">34</div><div class="line">35</div><div class="line">36</div><div class="line">37</div><div class="line">38</div><div class="line">39</div><div class="line">40</div><div class="line">41</div></pre></td><td class="code"><pre><div class="line">&lt;?xml version=<span class="string">"1.0"</span>?&gt;</div><div class="line">&lt;metainfo&gt;</div><div class="line"> &lt;schemaVersion&gt;2.0&lt;/schemaVersion&gt;</div><div class="line"> &lt;services&gt;</div><div class="line"> &lt;service&gt;</div><div class="line"> &lt;name&gt;SAMPLE&lt;/name&gt;</div><div class="line"> &lt;displayName&gt;My Sample&lt;/displayName&gt;</div><div class="line"> &lt;comment&gt;My v1 Sample&lt;/comment&gt;</div><div class="line"> &lt;version&gt;1.0&lt;/version&gt;</div><div class="line"> &lt;components&gt;</div><div class="line"> &lt;component&gt;</div><div class="line"> &lt;name&gt;MYMASTER&lt;/name&gt;</div><div class="line"> &lt;displayName&gt;My Master&lt;/displayName&gt;</div><div class="line"> &lt;category&gt;MASTER&lt;/category&gt;</div><div class="line"> &lt;cardinality&gt;1&lt;/cardinality&gt;</div><div class="line"> &lt;commandScript&gt;</div><div class="line"> &lt;script&gt;scripts/master.py&lt;/script&gt;</div><div class="line"> &lt;scriptType&gt;PYTHON&lt;/scriptType&gt;</div><div class="line"> &lt;timeout&gt;5000&lt;/timeout&gt;</div><div class="line"> &lt;/commandScript&gt;</div><div class="line"> &lt;/component&gt;</div><div class="line"> &lt;component&gt;</div><div class="line"> &lt;name&gt;MYSALVE&lt;/name&gt;</div><div class="line"> &lt;displayName&gt;My Slave&lt;/displayName&gt;</div><div class="line"> &lt;category&gt;SLAVE&lt;/category&gt;</div><div class="line"> &lt;cardinality&gt;1+&lt;/cardinality&gt;</div><div class="line"> &lt;commandScript&gt;</div><div class="line"> &lt;script&gt;scripts/slave.py&lt;/script&gt;</div><div class="line"> &lt;scriptType&gt;PYTHON&lt;/scriptType&gt;</div><div class="line"> &lt;timeout&gt;5000&lt;/timeout&gt;</div><div class="line"> &lt;/commandScript&gt;</div><div class="line"> &lt;/component&gt;</div><div class="line"> &lt;/components&gt;</div><div class="line"> &lt;osSpecifics&gt;</div><div class="line"> &lt;osSpecific&gt;</div><div class="line"> &lt;osFamily&gt;any&lt;/osFamily&gt;</div><div class="line"> &lt;/osSpecific&gt;</div><div class="line"> &lt;/osSpecifics&gt;</div><div class="line"> &lt;/service&gt;</div><div class="line"> &lt;/services&gt;</div><div class="line">&lt;/metainfo&gt;</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第三步，需要创建 Service 的控制脚本。这里我们需要在 SAMPLE 底下创建一个 package 目录，然后在 package 底下创建目录 scripts ，进而创建 master.py 和 slave.py。这里需要保证脚本路径和上一步中 metainfo.xml 中的配置路径是一致的。这两个 Python 脚本是用来控制 Master 和 Slave 模块的生命周期。脚本中函数的含义也如其名字一样：install 就是安装调用的接口；start、stop 分别就是启停的调用；Status 是定期检查 component 状态的调用；Configure 是安装完成配置该模块的调用。示例目录结构如下图。</p>
<p><strong>图 19. Sample Service 的目录结构</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/19.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Python 脚本的示例代码：</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Master.py：</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div><div class="line">24</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> sys, os</div><div class="line"><span class="keyword">from</span> resource_management <span class="keyword">import</span> *</div><div class="line"><span class="keyword">from</span> resource_management.core.exceptions <span class="keyword">import</span> ComponentIsNotRunning</div><div class="line"><span class="keyword">from</span> resource_management.core.environment <span class="keyword">import</span> Environment</div><div class="line"><span class="keyword">from</span> resource_management.core.logger <span class="keyword">import</span> Logger</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Master</span><span class="params">(Script)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">install</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Install My Master"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">configure</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Configure My Master"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">start</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Start My Master"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">stop</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Stop My Master"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">status</span><span class="params">(self, env)</span>:</span> </div><div class="line"> <span class="keyword">print</span> <span class="string">"Status..."</span></div><div class="line"></div><div class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</div><div class="line"> Master().execute()</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Slave.py:</p>
<figure class="highlight python"><table><tr><td class="gutter"><pre><div class="line">1</div><div class="line">2</div><div class="line">3</div><div class="line">4</div><div class="line">5</div><div class="line">6</div><div class="line">7</div><div class="line">8</div><div class="line">9</div><div class="line">10</div><div class="line">11</div><div class="line">12</div><div class="line">13</div><div class="line">14</div><div class="line">15</div><div class="line">16</div><div class="line">17</div><div class="line">18</div><div class="line">19</div><div class="line">20</div><div class="line">21</div><div class="line">22</div><div class="line">23</div></pre></td><td class="code"><pre><div class="line"><span class="keyword">import</span> sys, os</div><div class="line"><span class="keyword">from</span> resource_management <span class="keyword">import</span> *</div><div class="line"><span class="keyword">from</span> resource_management.core.exceptions <span class="keyword">import</span> ComponentIsNotRunning</div><div class="line"><span class="keyword">from</span> resource_management.core.environment <span class="keyword">import</span> Environment</div><div class="line"><span class="keyword">from</span> resource_management.core.logger <span class="keyword">import</span> Logger</div><div class="line"></div><div class="line"><span class="class"><span class="keyword">class</span> <span class="title">Slave</span><span class="params">(Script)</span>:</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">install</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Install My Slave"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">configure</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Configure My Slave"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">start</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Start My Slave"</span></div><div class="line"></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">stop</span><span class="params">(self, env)</span>:</span></div><div class="line"> <span class="keyword">print</span> <span class="string">"Stop My Slave"</span></div><div class="line"> <span class="function"><span class="keyword">def</span> <span class="title">status</span><span class="params">(self, env)</span>:</span> </div><div class="line"> <span class="keyword">print</span> <span class="string">"Status..."</span></div><div class="line"></div><div class="line"><span class="keyword">if</span> __name__ == <span class="string">"__main__"</span>:</div><div class="line"> Slave().execute()</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第四步，需要重启 Ambari Server。因为 Ambari Server 只有在重启的时候才会读取 Service 和 Stack 的配置。命令行执行：</p>
<figure class="highlight bash"><table><tr><td class="gutter"><pre><div class="line">1</div></pre></td><td class="code"><pre><div class="line">ambari-server restart</div></pre></td></tr></table></figure>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;第五步，登录 Ambari 的 GUI，点击左下角的 Action，选择 Add Service。如下图：</p>
<p><strong>图 20. Add Service 按钮</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/20.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;这时候就可以看到我们自定义的 Service：SAMPLE。如下图：</p>
<p><strong>图 21. Sample Service 列表</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/21.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;选择左侧 My Sample 后，就可以一路 Next 了，这个过程其实和我们在搭建 Hadoop2.x 集群的时候是类似的。由于这个 Service 没有真的安装包，所以安装过程会非常的快，启动命令也没有真正的逻辑，所以启动过程也是很快的。等最后点击完 Complete，整个安装过程也就结束了。再回到 Ambari 的 Dashboard 的时候，我们就可以看到这个 My Sample 了，如下图：</p>
<p><strong>图 22. My Sample 的 Service 页面</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/22.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;到此就可以和第四节中管理 Hadoop 集群一样管理我们的 My Sample。例如下图，Stop 我们的 My Sample。</p>
<p><strong>图 23. Stop Sample 页面 1</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/23.jpeg?raw=true" alt=""></figure></p>
<p><strong>图 24. Stop Sample 页面 2</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/24.jpeg?raw=true" alt=""></figure></p>
<p><strong>图 25. Stop Sample 页面 3</strong></p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/Ambari%E2%80%94%E2%80%94%E5%A4%A7%E6%95%B0%E6%8D%AE%E5%B9%B3%E5%8F%B0%E7%9A%84%E6%90%AD%E5%BB%BA%E5%88%A9%E5%99%A8/25.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;进阶的篇幅中，将会探讨如何给我们的 My Sample 自定义一些 Actions，以及 Action 之间的依赖关系如何定义。篇幅有限，这里就先到此为止。希望以上的介绍能够燃起大家对 Ambari 的热情。</p>
<h2 id="总结"><a href="#总结" class="headerlink" title="总结"></a>总结</h2><p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;大数据与云计算可谓是如今数据中心中最火的两项技术领域，几乎所有的 IT 服务商都想在这两项技术中有所建树。相信 Ambari 可以帮助一些 Hadoop 的初学者。长远看来，大数据的发展离不开云计算，云计算中 IaaS 可谓已经很成熟，并且价格低廉。这时候许多公司将目光聚集在了 PaaS。大数据的流行更是加速了相关 PaaS 产品的发展，而 Ambari 的出现必然可以拉近 IaaS 和 PaaS 的距离。也就是说有了 Ambari，或许再加上 Docker，那么快速从 IaaS 演进到 PaaS 就显得不是那么困难了。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;当然这里 Ambari 要跟 IaaS 更好的切合，还有个对手那就是 Sahara。它是另一个土生土长的 OpenStack 的子项目，其目的也是为了在 Openstack 上面快速搭建 Hadoop 等集群。期望着这些项目能够快速成长，将来对大家都有所帮助。</p>

	

	

</article>




	<article>
	
		<h1><a href="/2017/10/12/Hadoop/2. 2分钟读懂Hadoop和Spark的异同/">2分钟读懂Hadoop和Spark的异同</a></h1>
	
	<div class="article__infos">
		<span class="article__date">2017-10-12</span><br />
		
		
			<span class="article__tags">
			  	<a class="article__tag-link" href="/tags/Hadoop/">Hadoop</a>
			</span>
		
	</div>

	

	
		<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;谈到大数据，相信大家对Hadoop和Apache Spark这两个名字并不陌生。但我们往往对它们的理解只是提留在字面上，并没有对它们进行深入的思考，下面不妨跟我一块看下它们究竟有什么异同。</p>
<p><figure class="figure"><img src="https://github.com/hcldirgit/image/blob/master/2%E5%88%86%E9%92%9F%E8%AF%BB%E6%87%82Hadoop%E5%92%8CSpark%E7%9A%84%E5%BC%82%E5%90%8C/01.jpeg?raw=true" alt=""></figure></p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;解决问题的层面不一样</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;首先，Hadoop和Apache Spark两者都是大数据框架，但是各自存在的目的不尽相同。Hadoop实质上更多是一个分布式数据基础设施: 它将巨大的数据集分派到一个由普通计算机组成的集群中的多个节点进行存储，意味着您不需要购买和维护昂贵的服务器硬件。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;同时，Hadoop还会索引和跟踪这些数据，让大数据处理和分析效率达到前所未有的高度。Spark，则是那么一个专门用来对那些分布式存储的大数据进行处理的工具，它并不会进行分布式数据的存储。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;两者可合可分<br>Hadoop除了提供为大家所共识的HDFS分布式数据存储功能之外，还提供了叫做MapReduce的数据处理功能。所以这里我们完全可以抛开Spark，使用Hadoop自身的MapReduce来完成数据的处理。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;相反，Spark也不是非要依附在Hadoop身上才能生存。但如上所述，毕竟它没有提供文件管理系统，所以，它必须和其他的分布式文件系统进行集成才能运作。这里我们可以选择Hadoop的HDFS,也可以选择其他的基于云的数据系统平台。但Spark默认来说还是被用在Hadoop上面的，毕竟，大家都认为它们的结合是最好的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;以下是从网上摘录的对MapReduce的最简洁明了的解析:</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;我们要数图书馆中的所有书。你数1号书架，我数2号书架。这就是“Map”。我们人越多，数书就更快。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;现在我们到一起，把所有人的统计数加在一起。这就是“Reduce”。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spark数据处理速度秒杀MapReduce</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spark因为其处理数据的方式不一样，会比MapReduce快上很多。MapReduce是分步对数据进行处理的: ”从集群中读取数据，进行一次处理，将结果写到集群，从集群中读取更新后的数据，进行下一次的处理，将结果写到集群，等等…“ Booz Allen Hamilton的数据科学家Kirk Borne如此解析。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;反观Spark，它会在内存中以接近“实时”的时间完成所有的数据分析：“从集群中读取数据，完成所有必须的分析处理，将结果写回集群，完成，” Born说道。Spark的批处理速度比MapReduce快近10倍，内存中的数据分析速度则快近100倍。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;如果需要处理的数据和结果需求大部分情况下是静态的，且你也有耐心等待批处理的完成的话，MapReduce的处理方式也是完全可以接受的。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;但如果你需要对流数据进行分析，比如那些来自于工厂的传感器收集回来的数据，又或者说你的应用是需要多重数据处理的，那么你也许更应该使用Spark进行处理。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;大部分机器学习算法都是需要多重数据处理的。此外，通常会用到Spark的应用场景有以下方面：实时的市场活动，在线产品推荐，网络安全分析，机器日记监控等。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;灾难恢复</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;两者的灾难恢复方式迥异，但是都很不错。因为Hadoop将每次处理后的数据都写入到磁盘上，所以其天生就能很有弹性的对系统错误进行处理。</p>
<p>&#160;&#160;&#160;&#160;&#160;&#160;&#160;&#160;Spark的数据对象存储在分布于数据集群中的叫做弹性分布式数据集(RDD: Resilient Distributed Dataset)中。“这些数据对象既可以放在内存，也可以放在磁盘，所以RDD同样也可以提供完成的灾难恢复功能，”Borne指出。</p>

	

	

</article>





	<span class="different-posts">📖 <a href="/page/39">more posts</a> 📖</span>



	</main>

	<footer class="footer">
	<div class="footer-content">
		
	      <div class="footer__element">
	<p>Hi there, <br />welcome to my Blog glad you found it. Have a look around, will you?</p>
</div>

	    
	      <div class="footer__element">
	<h5>Check out</h5>
	<ul class="footer-links">
		<li class="footer-links__link"><a href="/archives">Archive</a></li>
		
		  <li class="footer-links__link"><a href="/atom.xml">RSS</a></li>
	    
		<li class="footer-links__link"><a href="/about">about page</a></li>
		<li class="footer-links__link"><a href="/tags">Tags</a></li>
		<li class="footer-links__link"><a href="/categories">Categories</a></li>
	</ul>
</div>

	    

		<div class="footer-credit">
			<span>© 2017 失落的乐章 | Powered by <a href="https://hexo.io/">Hexo</a> | Theme <a href="https://github.com/HoverBaum/meilidu-hexo">MeiliDu</a></span>
		</div>

	</div>


</footer>



</body>

</html>
